xref: /OK3568_Linux_fs/external/xserver/dix/ptrveloc.c (revision 4882a59341e53eb6f0b4789bf948001014eff981)
1*4882a593Smuzhiyun /*
2*4882a593Smuzhiyun  *
3*4882a593Smuzhiyun  * Copyright © 2006-2009 Simon Thum             simon dot thum at gmx dot de
4*4882a593Smuzhiyun  *
5*4882a593Smuzhiyun  * Permission is hereby granted, free of charge, to any person obtaining a
6*4882a593Smuzhiyun  * copy of this software and associated documentation files (the "Software"),
7*4882a593Smuzhiyun  * to deal in the Software without restriction, including without limitation
8*4882a593Smuzhiyun  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9*4882a593Smuzhiyun  * and/or sell copies of the Software, and to permit persons to whom the
10*4882a593Smuzhiyun  * Software is furnished to do so, subject to the following conditions:
11*4882a593Smuzhiyun  *
12*4882a593Smuzhiyun  * The above copyright notice and this permission notice (including the next
13*4882a593Smuzhiyun  * paragraph) shall be included in all copies or substantial portions of the
14*4882a593Smuzhiyun  * Software.
15*4882a593Smuzhiyun  *
16*4882a593Smuzhiyun  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17*4882a593Smuzhiyun  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18*4882a593Smuzhiyun  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
19*4882a593Smuzhiyun  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20*4882a593Smuzhiyun  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21*4882a593Smuzhiyun  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
22*4882a593Smuzhiyun  * DEALINGS IN THE SOFTWARE.
23*4882a593Smuzhiyun  */
24*4882a593Smuzhiyun 
25*4882a593Smuzhiyun #ifdef HAVE_DIX_CONFIG_H
26*4882a593Smuzhiyun #include <dix-config.h>
27*4882a593Smuzhiyun #endif
28*4882a593Smuzhiyun 
29*4882a593Smuzhiyun #include <math.h>
30*4882a593Smuzhiyun #include <ptrveloc.h>
31*4882a593Smuzhiyun #include <exevents.h>
32*4882a593Smuzhiyun #include <X11/Xatom.h>
33*4882a593Smuzhiyun #include <os.h>
34*4882a593Smuzhiyun 
35*4882a593Smuzhiyun #include <xserver-properties.h>
36*4882a593Smuzhiyun 
37*4882a593Smuzhiyun /*****************************************************************************
38*4882a593Smuzhiyun  * Predictable pointer acceleration
39*4882a593Smuzhiyun  *
40*4882a593Smuzhiyun  * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de)
41*4882a593Smuzhiyun  *
42*4882a593Smuzhiyun  * Serves 3 complementary functions:
43*4882a593Smuzhiyun  * 1) provide a sophisticated ballistic velocity estimate to improve
44*4882a593Smuzhiyun  *    the relation between velocity (of the device) and acceleration
45*4882a593Smuzhiyun  * 2) make arbitrary acceleration profiles possible
46*4882a593Smuzhiyun  * 3) decelerate by two means (constant and adaptive) if enabled
47*4882a593Smuzhiyun  *
48*4882a593Smuzhiyun  * Important concepts are the
49*4882a593Smuzhiyun  *
50*4882a593Smuzhiyun  * - Scheme
51*4882a593Smuzhiyun  *      which selects the basic algorithm
52*4882a593Smuzhiyun  *      (see devices.c/InitPointerAccelerationScheme)
53*4882a593Smuzhiyun  * - Profile
54*4882a593Smuzhiyun  *      which returns an acceleration
55*4882a593Smuzhiyun  *      for a given velocity
56*4882a593Smuzhiyun  *
57*4882a593Smuzhiyun  *  The profile can be selected by the user at runtime.
58*4882a593Smuzhiyun  *  The classic profile is intended to cleanly perform old-style
59*4882a593Smuzhiyun  *  function selection (threshold =/!= 0)
60*4882a593Smuzhiyun  *
61*4882a593Smuzhiyun  ****************************************************************************/
62*4882a593Smuzhiyun 
63*4882a593Smuzhiyun /* fwds */
64*4882a593Smuzhiyun static double
65*4882a593Smuzhiyun SimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, double velocity,
66*4882a593Smuzhiyun                     double threshold, double acc);
67*4882a593Smuzhiyun static PointerAccelerationProfileFunc
68*4882a593Smuzhiyun GetAccelerationProfile(DeviceVelocityPtr vel, int profile_num);
69*4882a593Smuzhiyun static BOOL
70*4882a593Smuzhiyun InitializePredictableAccelerationProperties(DeviceIntPtr,
71*4882a593Smuzhiyun                                             DeviceVelocityPtr,
72*4882a593Smuzhiyun                                             PredictableAccelSchemePtr);
73*4882a593Smuzhiyun static BOOL
74*4882a593Smuzhiyun DeletePredictableAccelerationProperties(DeviceIntPtr,
75*4882a593Smuzhiyun                                         PredictableAccelSchemePtr);
76*4882a593Smuzhiyun 
77*4882a593Smuzhiyun /*#define PTRACCEL_DEBUGGING*/
78*4882a593Smuzhiyun 
79*4882a593Smuzhiyun #ifdef PTRACCEL_DEBUGGING
80*4882a593Smuzhiyun #define DebugAccelF(...) ErrorFSigSafe("dix/ptraccel: " __VA_ARGS__)
81*4882a593Smuzhiyun #else
82*4882a593Smuzhiyun #define DebugAccelF(...)        /* */
83*4882a593Smuzhiyun #endif
84*4882a593Smuzhiyun 
85*4882a593Smuzhiyun /********************************
86*4882a593Smuzhiyun  *  Init/Uninit
87*4882a593Smuzhiyun  *******************************/
88*4882a593Smuzhiyun 
89*4882a593Smuzhiyun /* some int which is not a profile number */
90*4882a593Smuzhiyun #define PROFILE_UNINITIALIZE (-100)
91*4882a593Smuzhiyun 
92*4882a593Smuzhiyun /**
93*4882a593Smuzhiyun  * Init DeviceVelocity struct so it should match the average case
94*4882a593Smuzhiyun  */
95*4882a593Smuzhiyun void
InitVelocityData(DeviceVelocityPtr vel)96*4882a593Smuzhiyun InitVelocityData(DeviceVelocityPtr vel)
97*4882a593Smuzhiyun {
98*4882a593Smuzhiyun     memset(vel, 0, sizeof(DeviceVelocityRec));
99*4882a593Smuzhiyun 
100*4882a593Smuzhiyun     vel->corr_mul = 10.0;       /* dots per 10 milisecond should be usable */
101*4882a593Smuzhiyun     vel->const_acceleration = 1.0;      /* no acceleration/deceleration  */
102*4882a593Smuzhiyun     vel->reset_time = 300;
103*4882a593Smuzhiyun     vel->use_softening = 1;
104*4882a593Smuzhiyun     vel->min_acceleration = 1.0;        /* don't decelerate */
105*4882a593Smuzhiyun     vel->max_rel_diff = 0.2;
106*4882a593Smuzhiyun     vel->max_diff = 1.0;
107*4882a593Smuzhiyun     vel->initial_range = 2;
108*4882a593Smuzhiyun     vel->average_accel = TRUE;
109*4882a593Smuzhiyun     SetAccelerationProfile(vel, AccelProfileClassic);
110*4882a593Smuzhiyun     InitTrackers(vel, 16);
111*4882a593Smuzhiyun }
112*4882a593Smuzhiyun 
113*4882a593Smuzhiyun /**
114*4882a593Smuzhiyun  * Clean up DeviceVelocityRec
115*4882a593Smuzhiyun  */
116*4882a593Smuzhiyun void
FreeVelocityData(DeviceVelocityPtr vel)117*4882a593Smuzhiyun FreeVelocityData(DeviceVelocityPtr vel)
118*4882a593Smuzhiyun {
119*4882a593Smuzhiyun     free(vel->tracker);
120*4882a593Smuzhiyun     SetAccelerationProfile(vel, PROFILE_UNINITIALIZE);
121*4882a593Smuzhiyun }
122*4882a593Smuzhiyun 
123*4882a593Smuzhiyun /**
124*4882a593Smuzhiyun  * Init predictable scheme
125*4882a593Smuzhiyun  */
126*4882a593Smuzhiyun Bool
InitPredictableAccelerationScheme(DeviceIntPtr dev,ValuatorAccelerationPtr protoScheme)127*4882a593Smuzhiyun InitPredictableAccelerationScheme(DeviceIntPtr dev,
128*4882a593Smuzhiyun                                   ValuatorAccelerationPtr protoScheme)
129*4882a593Smuzhiyun {
130*4882a593Smuzhiyun     DeviceVelocityPtr vel;
131*4882a593Smuzhiyun     ValuatorAccelerationRec scheme;
132*4882a593Smuzhiyun     PredictableAccelSchemePtr schemeData;
133*4882a593Smuzhiyun 
134*4882a593Smuzhiyun     scheme = *protoScheme;
135*4882a593Smuzhiyun     vel = calloc(1, sizeof(DeviceVelocityRec));
136*4882a593Smuzhiyun     schemeData = calloc(1, sizeof(PredictableAccelSchemeRec));
137*4882a593Smuzhiyun     if (!vel || !schemeData) {
138*4882a593Smuzhiyun         free(vel);
139*4882a593Smuzhiyun         free(schemeData);
140*4882a593Smuzhiyun         return FALSE;
141*4882a593Smuzhiyun     }
142*4882a593Smuzhiyun     InitVelocityData(vel);
143*4882a593Smuzhiyun     schemeData->vel = vel;
144*4882a593Smuzhiyun     scheme.accelData = schemeData;
145*4882a593Smuzhiyun     if (!InitializePredictableAccelerationProperties(dev, vel, schemeData)) {
146*4882a593Smuzhiyun         free(vel);
147*4882a593Smuzhiyun         free(schemeData);
148*4882a593Smuzhiyun         return FALSE;
149*4882a593Smuzhiyun     }
150*4882a593Smuzhiyun     /* all fine, assign scheme to device */
151*4882a593Smuzhiyun     dev->valuator->accelScheme = scheme;
152*4882a593Smuzhiyun     return TRUE;
153*4882a593Smuzhiyun }
154*4882a593Smuzhiyun 
155*4882a593Smuzhiyun /**
156*4882a593Smuzhiyun  *  Uninit scheme
157*4882a593Smuzhiyun  */
158*4882a593Smuzhiyun void
AccelerationDefaultCleanup(DeviceIntPtr dev)159*4882a593Smuzhiyun AccelerationDefaultCleanup(DeviceIntPtr dev)
160*4882a593Smuzhiyun {
161*4882a593Smuzhiyun     DeviceVelocityPtr vel = GetDevicePredictableAccelData(dev);
162*4882a593Smuzhiyun 
163*4882a593Smuzhiyun     if (vel) {
164*4882a593Smuzhiyun         /* the proper guarantee would be that we're not inside of
165*4882a593Smuzhiyun          * AccelSchemeProc(), but that seems impossible. Schemes don't get
166*4882a593Smuzhiyun          * switched often anyway.
167*4882a593Smuzhiyun          */
168*4882a593Smuzhiyun         input_lock();
169*4882a593Smuzhiyun         dev->valuator->accelScheme.AccelSchemeProc = NULL;
170*4882a593Smuzhiyun         FreeVelocityData(vel);
171*4882a593Smuzhiyun         free(vel);
172*4882a593Smuzhiyun         DeletePredictableAccelerationProperties(dev,
173*4882a593Smuzhiyun                                                 (PredictableAccelSchemePtr)
174*4882a593Smuzhiyun                                                 dev->valuator->accelScheme.
175*4882a593Smuzhiyun                                                 accelData);
176*4882a593Smuzhiyun         free(dev->valuator->accelScheme.accelData);
177*4882a593Smuzhiyun         dev->valuator->accelScheme.accelData = NULL;
178*4882a593Smuzhiyun         input_unlock();
179*4882a593Smuzhiyun     }
180*4882a593Smuzhiyun }
181*4882a593Smuzhiyun 
182*4882a593Smuzhiyun /*************************
183*4882a593Smuzhiyun  * Input property support
184*4882a593Smuzhiyun  ************************/
185*4882a593Smuzhiyun 
186*4882a593Smuzhiyun /**
187*4882a593Smuzhiyun  * choose profile
188*4882a593Smuzhiyun  */
189*4882a593Smuzhiyun static int
AccelSetProfileProperty(DeviceIntPtr dev,Atom atom,XIPropertyValuePtr val,BOOL checkOnly)190*4882a593Smuzhiyun AccelSetProfileProperty(DeviceIntPtr dev, Atom atom,
191*4882a593Smuzhiyun                         XIPropertyValuePtr val, BOOL checkOnly)
192*4882a593Smuzhiyun {
193*4882a593Smuzhiyun     DeviceVelocityPtr vel;
194*4882a593Smuzhiyun     int profile, *ptr = &profile;
195*4882a593Smuzhiyun     int rc;
196*4882a593Smuzhiyun     int nelem = 1;
197*4882a593Smuzhiyun 
198*4882a593Smuzhiyun     if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER))
199*4882a593Smuzhiyun         return Success;
200*4882a593Smuzhiyun 
201*4882a593Smuzhiyun     vel = GetDevicePredictableAccelData(dev);
202*4882a593Smuzhiyun     if (!vel)
203*4882a593Smuzhiyun         return BadValue;
204*4882a593Smuzhiyun     rc = XIPropToInt(val, &nelem, &ptr);
205*4882a593Smuzhiyun 
206*4882a593Smuzhiyun     if (checkOnly) {
207*4882a593Smuzhiyun         if (rc)
208*4882a593Smuzhiyun             return rc;
209*4882a593Smuzhiyun 
210*4882a593Smuzhiyun         if (GetAccelerationProfile(vel, profile) == NULL)
211*4882a593Smuzhiyun             return BadValue;
212*4882a593Smuzhiyun     }
213*4882a593Smuzhiyun     else
214*4882a593Smuzhiyun         SetAccelerationProfile(vel, profile);
215*4882a593Smuzhiyun 
216*4882a593Smuzhiyun     return Success;
217*4882a593Smuzhiyun }
218*4882a593Smuzhiyun 
219*4882a593Smuzhiyun static long
AccelInitProfileProperty(DeviceIntPtr dev,DeviceVelocityPtr vel)220*4882a593Smuzhiyun AccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
221*4882a593Smuzhiyun {
222*4882a593Smuzhiyun     int profile = vel->statistics.profile_number;
223*4882a593Smuzhiyun     Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
224*4882a593Smuzhiyun 
225*4882a593Smuzhiyun     XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32,
226*4882a593Smuzhiyun                            PropModeReplace, 1, &profile, FALSE);
227*4882a593Smuzhiyun     XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE);
228*4882a593Smuzhiyun     return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL);
229*4882a593Smuzhiyun }
230*4882a593Smuzhiyun 
231*4882a593Smuzhiyun /**
232*4882a593Smuzhiyun  * constant deceleration
233*4882a593Smuzhiyun  */
234*4882a593Smuzhiyun static int
AccelSetDecelProperty(DeviceIntPtr dev,Atom atom,XIPropertyValuePtr val,BOOL checkOnly)235*4882a593Smuzhiyun AccelSetDecelProperty(DeviceIntPtr dev, Atom atom,
236*4882a593Smuzhiyun                       XIPropertyValuePtr val, BOOL checkOnly)
237*4882a593Smuzhiyun {
238*4882a593Smuzhiyun     DeviceVelocityPtr vel;
239*4882a593Smuzhiyun     float v, *ptr = &v;
240*4882a593Smuzhiyun     int rc;
241*4882a593Smuzhiyun     int nelem = 1;
242*4882a593Smuzhiyun 
243*4882a593Smuzhiyun     if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION))
244*4882a593Smuzhiyun         return Success;
245*4882a593Smuzhiyun 
246*4882a593Smuzhiyun     vel = GetDevicePredictableAccelData(dev);
247*4882a593Smuzhiyun     if (!vel)
248*4882a593Smuzhiyun         return BadValue;
249*4882a593Smuzhiyun     rc = XIPropToFloat(val, &nelem, &ptr);
250*4882a593Smuzhiyun 
251*4882a593Smuzhiyun     if (checkOnly) {
252*4882a593Smuzhiyun         if (rc)
253*4882a593Smuzhiyun             return rc;
254*4882a593Smuzhiyun         return (v > 0) ? Success : BadValue;
255*4882a593Smuzhiyun     }
256*4882a593Smuzhiyun 
257*4882a593Smuzhiyun     vel->const_acceleration = 1 / v;
258*4882a593Smuzhiyun 
259*4882a593Smuzhiyun     return Success;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun 
262*4882a593Smuzhiyun static long
AccelInitDecelProperty(DeviceIntPtr dev,DeviceVelocityPtr vel)263*4882a593Smuzhiyun AccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
264*4882a593Smuzhiyun {
265*4882a593Smuzhiyun     float fval = 1.0 / vel->const_acceleration;
266*4882a593Smuzhiyun     Atom prop_const_decel =
267*4882a593Smuzhiyun         XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
268*4882a593Smuzhiyun     XIChangeDeviceProperty(dev, prop_const_decel,
269*4882a593Smuzhiyun                            XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
270*4882a593Smuzhiyun                            1, &fval, FALSE);
271*4882a593Smuzhiyun     XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE);
272*4882a593Smuzhiyun     return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL);
273*4882a593Smuzhiyun }
274*4882a593Smuzhiyun 
275*4882a593Smuzhiyun /**
276*4882a593Smuzhiyun  * adaptive deceleration
277*4882a593Smuzhiyun  */
278*4882a593Smuzhiyun static int
AccelSetAdaptDecelProperty(DeviceIntPtr dev,Atom atom,XIPropertyValuePtr val,BOOL checkOnly)279*4882a593Smuzhiyun AccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom,
280*4882a593Smuzhiyun                            XIPropertyValuePtr val, BOOL checkOnly)
281*4882a593Smuzhiyun {
282*4882a593Smuzhiyun     DeviceVelocityPtr veloc;
283*4882a593Smuzhiyun     float v, *ptr = &v;
284*4882a593Smuzhiyun     int rc;
285*4882a593Smuzhiyun     int nelem = 1;
286*4882a593Smuzhiyun 
287*4882a593Smuzhiyun     if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION))
288*4882a593Smuzhiyun         return Success;
289*4882a593Smuzhiyun 
290*4882a593Smuzhiyun     veloc = GetDevicePredictableAccelData(dev);
291*4882a593Smuzhiyun     if (!veloc)
292*4882a593Smuzhiyun         return BadValue;
293*4882a593Smuzhiyun     rc = XIPropToFloat(val, &nelem, &ptr);
294*4882a593Smuzhiyun 
295*4882a593Smuzhiyun     if (checkOnly) {
296*4882a593Smuzhiyun         if (rc)
297*4882a593Smuzhiyun             return rc;
298*4882a593Smuzhiyun         return (v >= 1.0f) ? Success : BadValue;
299*4882a593Smuzhiyun     }
300*4882a593Smuzhiyun 
301*4882a593Smuzhiyun     if (v >= 1.0f)
302*4882a593Smuzhiyun         veloc->min_acceleration = 1 / v;
303*4882a593Smuzhiyun 
304*4882a593Smuzhiyun     return Success;
305*4882a593Smuzhiyun }
306*4882a593Smuzhiyun 
307*4882a593Smuzhiyun static long
AccelInitAdaptDecelProperty(DeviceIntPtr dev,DeviceVelocityPtr vel)308*4882a593Smuzhiyun AccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
309*4882a593Smuzhiyun {
310*4882a593Smuzhiyun     float fval = 1.0 / vel->min_acceleration;
311*4882a593Smuzhiyun     Atom prop_adapt_decel =
312*4882a593Smuzhiyun         XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
313*4882a593Smuzhiyun 
314*4882a593Smuzhiyun     XIChangeDeviceProperty(dev, prop_adapt_decel,
315*4882a593Smuzhiyun                            XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
316*4882a593Smuzhiyun                            1, &fval, FALSE);
317*4882a593Smuzhiyun     XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE);
318*4882a593Smuzhiyun     return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL,
319*4882a593Smuzhiyun                                      NULL);
320*4882a593Smuzhiyun }
321*4882a593Smuzhiyun 
322*4882a593Smuzhiyun /**
323*4882a593Smuzhiyun  * velocity scaling
324*4882a593Smuzhiyun  */
325*4882a593Smuzhiyun static int
AccelSetScaleProperty(DeviceIntPtr dev,Atom atom,XIPropertyValuePtr val,BOOL checkOnly)326*4882a593Smuzhiyun AccelSetScaleProperty(DeviceIntPtr dev, Atom atom,
327*4882a593Smuzhiyun                       XIPropertyValuePtr val, BOOL checkOnly)
328*4882a593Smuzhiyun {
329*4882a593Smuzhiyun     DeviceVelocityPtr vel;
330*4882a593Smuzhiyun     float v, *ptr = &v;
331*4882a593Smuzhiyun     int rc;
332*4882a593Smuzhiyun     int nelem = 1;
333*4882a593Smuzhiyun 
334*4882a593Smuzhiyun     if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING))
335*4882a593Smuzhiyun         return Success;
336*4882a593Smuzhiyun 
337*4882a593Smuzhiyun     vel = GetDevicePredictableAccelData(dev);
338*4882a593Smuzhiyun     if (!vel)
339*4882a593Smuzhiyun         return BadValue;
340*4882a593Smuzhiyun     rc = XIPropToFloat(val, &nelem, &ptr);
341*4882a593Smuzhiyun 
342*4882a593Smuzhiyun     if (checkOnly) {
343*4882a593Smuzhiyun         if (rc)
344*4882a593Smuzhiyun             return rc;
345*4882a593Smuzhiyun 
346*4882a593Smuzhiyun         return (v > 0) ? Success : BadValue;
347*4882a593Smuzhiyun     }
348*4882a593Smuzhiyun 
349*4882a593Smuzhiyun     if (v > 0)
350*4882a593Smuzhiyun         vel->corr_mul = v;
351*4882a593Smuzhiyun 
352*4882a593Smuzhiyun     return Success;
353*4882a593Smuzhiyun }
354*4882a593Smuzhiyun 
355*4882a593Smuzhiyun static long
AccelInitScaleProperty(DeviceIntPtr dev,DeviceVelocityPtr vel)356*4882a593Smuzhiyun AccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel)
357*4882a593Smuzhiyun {
358*4882a593Smuzhiyun     float fval = vel->corr_mul;
359*4882a593Smuzhiyun     Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
360*4882a593Smuzhiyun 
361*4882a593Smuzhiyun     XIChangeDeviceProperty(dev, prop_velo_scale,
362*4882a593Smuzhiyun                            XIGetKnownProperty(XATOM_FLOAT), 32, PropModeReplace,
363*4882a593Smuzhiyun                            1, &fval, FALSE);
364*4882a593Smuzhiyun     XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE);
365*4882a593Smuzhiyun     return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL);
366*4882a593Smuzhiyun }
367*4882a593Smuzhiyun 
368*4882a593Smuzhiyun static BOOL
InitializePredictableAccelerationProperties(DeviceIntPtr dev,DeviceVelocityPtr vel,PredictableAccelSchemePtr schemeData)369*4882a593Smuzhiyun InitializePredictableAccelerationProperties(DeviceIntPtr dev,
370*4882a593Smuzhiyun                                             DeviceVelocityPtr vel,
371*4882a593Smuzhiyun                                             PredictableAccelSchemePtr
372*4882a593Smuzhiyun                                             schemeData)
373*4882a593Smuzhiyun {
374*4882a593Smuzhiyun     int num_handlers = 4;
375*4882a593Smuzhiyun 
376*4882a593Smuzhiyun     if (!vel)
377*4882a593Smuzhiyun         return FALSE;
378*4882a593Smuzhiyun 
379*4882a593Smuzhiyun     schemeData->prop_handlers = calloc(num_handlers, sizeof(long));
380*4882a593Smuzhiyun     if (!schemeData->prop_handlers)
381*4882a593Smuzhiyun         return FALSE;
382*4882a593Smuzhiyun     schemeData->num_prop_handlers = num_handlers;
383*4882a593Smuzhiyun     schemeData->prop_handlers[0] = AccelInitProfileProperty(dev, vel);
384*4882a593Smuzhiyun     schemeData->prop_handlers[1] = AccelInitDecelProperty(dev, vel);
385*4882a593Smuzhiyun     schemeData->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel);
386*4882a593Smuzhiyun     schemeData->prop_handlers[3] = AccelInitScaleProperty(dev, vel);
387*4882a593Smuzhiyun 
388*4882a593Smuzhiyun     return TRUE;
389*4882a593Smuzhiyun }
390*4882a593Smuzhiyun 
391*4882a593Smuzhiyun BOOL
DeletePredictableAccelerationProperties(DeviceIntPtr dev,PredictableAccelSchemePtr scheme)392*4882a593Smuzhiyun DeletePredictableAccelerationProperties(DeviceIntPtr dev,
393*4882a593Smuzhiyun                                         PredictableAccelSchemePtr scheme)
394*4882a593Smuzhiyun {
395*4882a593Smuzhiyun     DeviceVelocityPtr vel;
396*4882a593Smuzhiyun     Atom prop;
397*4882a593Smuzhiyun     int i;
398*4882a593Smuzhiyun 
399*4882a593Smuzhiyun     prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING);
400*4882a593Smuzhiyun     XIDeleteDeviceProperty(dev, prop, FALSE);
401*4882a593Smuzhiyun     prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION);
402*4882a593Smuzhiyun     XIDeleteDeviceProperty(dev, prop, FALSE);
403*4882a593Smuzhiyun     prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION);
404*4882a593Smuzhiyun     XIDeleteDeviceProperty(dev, prop, FALSE);
405*4882a593Smuzhiyun     prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER);
406*4882a593Smuzhiyun     XIDeleteDeviceProperty(dev, prop, FALSE);
407*4882a593Smuzhiyun 
408*4882a593Smuzhiyun     vel = GetDevicePredictableAccelData(dev);
409*4882a593Smuzhiyun     if (vel) {
410*4882a593Smuzhiyun         for (i = 0; i < scheme->num_prop_handlers; i++)
411*4882a593Smuzhiyun             if (scheme->prop_handlers[i])
412*4882a593Smuzhiyun                 XIUnregisterPropertyHandler(dev, scheme->prop_handlers[i]);
413*4882a593Smuzhiyun     }
414*4882a593Smuzhiyun 
415*4882a593Smuzhiyun     free(scheme->prop_handlers);
416*4882a593Smuzhiyun     scheme->prop_handlers = NULL;
417*4882a593Smuzhiyun     scheme->num_prop_handlers = 0;
418*4882a593Smuzhiyun     return TRUE;
419*4882a593Smuzhiyun }
420*4882a593Smuzhiyun 
421*4882a593Smuzhiyun /*********************
422*4882a593Smuzhiyun  * Tracking logic
423*4882a593Smuzhiyun  ********************/
424*4882a593Smuzhiyun 
425*4882a593Smuzhiyun void
InitTrackers(DeviceVelocityPtr vel,int ntracker)426*4882a593Smuzhiyun InitTrackers(DeviceVelocityPtr vel, int ntracker)
427*4882a593Smuzhiyun {
428*4882a593Smuzhiyun     if (ntracker < 1) {
429*4882a593Smuzhiyun         ErrorF("invalid number of trackers\n");
430*4882a593Smuzhiyun         return;
431*4882a593Smuzhiyun     }
432*4882a593Smuzhiyun     free(vel->tracker);
433*4882a593Smuzhiyun     vel->tracker = (MotionTrackerPtr) calloc(ntracker, sizeof(MotionTracker));
434*4882a593Smuzhiyun     vel->num_tracker = ntracker;
435*4882a593Smuzhiyun }
436*4882a593Smuzhiyun 
437*4882a593Smuzhiyun enum directions {
438*4882a593Smuzhiyun     N = (1 << 0),
439*4882a593Smuzhiyun     NE = (1 << 1),
440*4882a593Smuzhiyun     E = (1 << 2),
441*4882a593Smuzhiyun     SE = (1 << 3),
442*4882a593Smuzhiyun     S = (1 << 4),
443*4882a593Smuzhiyun     SW = (1 << 5),
444*4882a593Smuzhiyun     W = (1 << 6),
445*4882a593Smuzhiyun     NW = (1 << 7),
446*4882a593Smuzhiyun     UNDEFINED = 0xFF
447*4882a593Smuzhiyun };
448*4882a593Smuzhiyun 
449*4882a593Smuzhiyun /**
450*4882a593Smuzhiyun  * return a bit field of possible directions.
451*4882a593Smuzhiyun  * There's no reason against widening to more precise directions (<45 degrees),
452*4882a593Smuzhiyun  * should it not perform well. All this is needed for is sort out non-linear
453*4882a593Smuzhiyun  * motion, so precision isn't paramount. However, one should not flag direction
454*4882a593Smuzhiyun  * too narrow, since it would then cut the linear segment to zero size way too
455*4882a593Smuzhiyun  * often.
456*4882a593Smuzhiyun  *
457*4882a593Smuzhiyun  * @return A bitmask for N, NE, S, SE, etc. indicating the directions for
458*4882a593Smuzhiyun  * this movement.
459*4882a593Smuzhiyun  */
460*4882a593Smuzhiyun static int
DoGetDirection(int dx,int dy)461*4882a593Smuzhiyun DoGetDirection(int dx, int dy)
462*4882a593Smuzhiyun {
463*4882a593Smuzhiyun     int dir = 0;
464*4882a593Smuzhiyun 
465*4882a593Smuzhiyun     /* on insignificant mickeys, flag 135 degrees */
466*4882a593Smuzhiyun     if (abs(dx) < 2 && abs(dy) < 2) {
467*4882a593Smuzhiyun         /* first check diagonal cases */
468*4882a593Smuzhiyun         if (dx > 0 && dy > 0)
469*4882a593Smuzhiyun             dir = E | SE | S;
470*4882a593Smuzhiyun         else if (dx > 0 && dy < 0)
471*4882a593Smuzhiyun             dir = N | NE | E;
472*4882a593Smuzhiyun         else if (dx < 0 && dy < 0)
473*4882a593Smuzhiyun             dir = W | NW | N;
474*4882a593Smuzhiyun         else if (dx < 0 && dy > 0)
475*4882a593Smuzhiyun             dir = W | SW | S;
476*4882a593Smuzhiyun         /* check axis-aligned directions */
477*4882a593Smuzhiyun         else if (dx > 0)
478*4882a593Smuzhiyun             dir = NE | E | SE;
479*4882a593Smuzhiyun         else if (dx < 0)
480*4882a593Smuzhiyun             dir = NW | W | SW;
481*4882a593Smuzhiyun         else if (dy > 0)
482*4882a593Smuzhiyun             dir = SE | S | SW;
483*4882a593Smuzhiyun         else if (dy < 0)
484*4882a593Smuzhiyun             dir = NE | N | NW;
485*4882a593Smuzhiyun         else
486*4882a593Smuzhiyun             dir = UNDEFINED;    /* shouldn't happen */
487*4882a593Smuzhiyun     }
488*4882a593Smuzhiyun     else {                      /* compute angle and set appropriate flags */
489*4882a593Smuzhiyun         double r;
490*4882a593Smuzhiyun         int i1, i2;
491*4882a593Smuzhiyun 
492*4882a593Smuzhiyun         r = atan2(dy, dx);
493*4882a593Smuzhiyun         /* find direction.
494*4882a593Smuzhiyun          *
495*4882a593Smuzhiyun          * Add 360° to avoid r become negative since C has no well-defined
496*4882a593Smuzhiyun          * modulo for such cases. Then divide by 45° to get the octant
497*4882a593Smuzhiyun          * number,  e.g.
498*4882a593Smuzhiyun          *          0 <= r <= 1 is [0-45]°
499*4882a593Smuzhiyun          *          1 <= r <= 2 is [45-90]°
500*4882a593Smuzhiyun          *          etc.
501*4882a593Smuzhiyun          * But we add extra 90° to match up with our N, S, etc. defines up
502*4882a593Smuzhiyun          * there, rest stays the same.
503*4882a593Smuzhiyun          */
504*4882a593Smuzhiyun         r = (r + (M_PI * 2.5)) / (M_PI / 4);
505*4882a593Smuzhiyun         /* this intends to flag 2 directions (45 degrees),
506*4882a593Smuzhiyun          * except on very well-aligned mickeys. */
507*4882a593Smuzhiyun         i1 = (int) (r + 0.1) % 8;
508*4882a593Smuzhiyun         i2 = (int) (r + 0.9) % 8;
509*4882a593Smuzhiyun         if (i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7)
510*4882a593Smuzhiyun             dir = UNDEFINED;    /* shouldn't happen */
511*4882a593Smuzhiyun         else
512*4882a593Smuzhiyun             dir = (1 << i1 | 1 << i2);
513*4882a593Smuzhiyun     }
514*4882a593Smuzhiyun     return dir;
515*4882a593Smuzhiyun }
516*4882a593Smuzhiyun 
517*4882a593Smuzhiyun #define DIRECTION_CACHE_RANGE 5
518*4882a593Smuzhiyun #define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1)
519*4882a593Smuzhiyun 
520*4882a593Smuzhiyun /* cache DoGetDirection().
521*4882a593Smuzhiyun  * To avoid excessive use of direction calculation, cache the values for
522*4882a593Smuzhiyun  * [-5..5] for both x/y. Anything outside of that is calcualted on the fly.
523*4882a593Smuzhiyun  *
524*4882a593Smuzhiyun  * @return A bitmask for N, NE, S, SE, etc. indicating the directions for
525*4882a593Smuzhiyun  * this movement.
526*4882a593Smuzhiyun  */
527*4882a593Smuzhiyun static int
GetDirection(int dx,int dy)528*4882a593Smuzhiyun GetDirection(int dx, int dy)
529*4882a593Smuzhiyun {
530*4882a593Smuzhiyun     static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE];
531*4882a593Smuzhiyun     int dir;
532*4882a593Smuzhiyun 
533*4882a593Smuzhiyun     if (abs(dx) <= DIRECTION_CACHE_RANGE && abs(dy) <= DIRECTION_CACHE_RANGE) {
534*4882a593Smuzhiyun         /* cacheable */
535*4882a593Smuzhiyun         dir = cache[DIRECTION_CACHE_RANGE + dx][DIRECTION_CACHE_RANGE + dy];
536*4882a593Smuzhiyun         if (dir == 0) {
537*4882a593Smuzhiyun             dir = DoGetDirection(dx, dy);
538*4882a593Smuzhiyun             cache[DIRECTION_CACHE_RANGE + dx][DIRECTION_CACHE_RANGE + dy] = dir;
539*4882a593Smuzhiyun         }
540*4882a593Smuzhiyun     }
541*4882a593Smuzhiyun     else {
542*4882a593Smuzhiyun         /* non-cacheable */
543*4882a593Smuzhiyun         dir = DoGetDirection(dx, dy);
544*4882a593Smuzhiyun     }
545*4882a593Smuzhiyun 
546*4882a593Smuzhiyun     return dir;
547*4882a593Smuzhiyun }
548*4882a593Smuzhiyun 
549*4882a593Smuzhiyun #undef DIRECTION_CACHE_RANGE
550*4882a593Smuzhiyun #undef DIRECTION_CACHE_SIZE
551*4882a593Smuzhiyun 
552*4882a593Smuzhiyun /* convert offset (age) to array index */
553*4882a593Smuzhiyun #define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker)
554*4882a593Smuzhiyun #define TRACKER(s, d) &(s)->tracker[TRACKER_INDEX(s,d)]
555*4882a593Smuzhiyun 
556*4882a593Smuzhiyun /**
557*4882a593Smuzhiyun  * Add the delta motion to each tracker, then reset the latest tracker to
558*4882a593Smuzhiyun  * 0/0 and set it as the current one.
559*4882a593Smuzhiyun  */
560*4882a593Smuzhiyun static inline void
FeedTrackers(DeviceVelocityPtr vel,double dx,double dy,int cur_t)561*4882a593Smuzhiyun FeedTrackers(DeviceVelocityPtr vel, double dx, double dy, int cur_t)
562*4882a593Smuzhiyun {
563*4882a593Smuzhiyun     int n;
564*4882a593Smuzhiyun 
565*4882a593Smuzhiyun     for (n = 0; n < vel->num_tracker; n++) {
566*4882a593Smuzhiyun         vel->tracker[n].dx += dx;
567*4882a593Smuzhiyun         vel->tracker[n].dy += dy;
568*4882a593Smuzhiyun     }
569*4882a593Smuzhiyun     n = (vel->cur_tracker + 1) % vel->num_tracker;
570*4882a593Smuzhiyun     vel->tracker[n].dx = 0.0;
571*4882a593Smuzhiyun     vel->tracker[n].dy = 0.0;
572*4882a593Smuzhiyun     vel->tracker[n].time = cur_t;
573*4882a593Smuzhiyun     vel->tracker[n].dir = GetDirection(dx, dy);
574*4882a593Smuzhiyun     DebugAccelF("motion [dx: %f dy: %f dir:%d diff: %d]\n",
575*4882a593Smuzhiyun                 dx, dy, vel->tracker[n].dir,
576*4882a593Smuzhiyun                 cur_t - vel->tracker[vel->cur_tracker].time);
577*4882a593Smuzhiyun     vel->cur_tracker = n;
578*4882a593Smuzhiyun }
579*4882a593Smuzhiyun 
580*4882a593Smuzhiyun /**
581*4882a593Smuzhiyun  * calc velocity for given tracker, with
582*4882a593Smuzhiyun  * velocity scaling.
583*4882a593Smuzhiyun  * This assumes linear motion.
584*4882a593Smuzhiyun  */
585*4882a593Smuzhiyun static double
CalcTracker(const MotionTracker * tracker,int cur_t)586*4882a593Smuzhiyun CalcTracker(const MotionTracker * tracker, int cur_t)
587*4882a593Smuzhiyun {
588*4882a593Smuzhiyun     double dist = sqrt(tracker->dx * tracker->dx + tracker->dy * tracker->dy);
589*4882a593Smuzhiyun     int dtime = cur_t - tracker->time;
590*4882a593Smuzhiyun 
591*4882a593Smuzhiyun     if (dtime > 0)
592*4882a593Smuzhiyun         return dist / dtime;
593*4882a593Smuzhiyun     else
594*4882a593Smuzhiyun         return 0;               /* synonymous for NaN, since we're not C99 */
595*4882a593Smuzhiyun }
596*4882a593Smuzhiyun 
597*4882a593Smuzhiyun /* find the most plausible velocity. That is, the most distant
598*4882a593Smuzhiyun  * (in time) tracker which isn't too old, the movement vector was
599*4882a593Smuzhiyun  * in the same octant, and where the velocity is within an
600*4882a593Smuzhiyun  * acceptable range to the inital velocity.
601*4882a593Smuzhiyun  *
602*4882a593Smuzhiyun  * @return The tracker's velocity or 0 if the above conditions are unmet
603*4882a593Smuzhiyun  */
604*4882a593Smuzhiyun static double
QueryTrackers(DeviceVelocityPtr vel,int cur_t)605*4882a593Smuzhiyun QueryTrackers(DeviceVelocityPtr vel, int cur_t)
606*4882a593Smuzhiyun {
607*4882a593Smuzhiyun     int offset, dir = UNDEFINED, used_offset = -1, age_ms;
608*4882a593Smuzhiyun 
609*4882a593Smuzhiyun     /* initial velocity: a low-offset, valid velocity */
610*4882a593Smuzhiyun     double initial_velocity = 0, result = 0, velocity_diff;
611*4882a593Smuzhiyun     double velocity_factor = vel->corr_mul * vel->const_acceleration;   /* premultiply */
612*4882a593Smuzhiyun 
613*4882a593Smuzhiyun     /* loop from current to older data */
614*4882a593Smuzhiyun     for (offset = 1; offset < vel->num_tracker; offset++) {
615*4882a593Smuzhiyun         MotionTracker *tracker = TRACKER(vel, offset);
616*4882a593Smuzhiyun         double tracker_velocity;
617*4882a593Smuzhiyun 
618*4882a593Smuzhiyun         age_ms = cur_t - tracker->time;
619*4882a593Smuzhiyun 
620*4882a593Smuzhiyun         /* bail out if data is too old and protect from overrun */
621*4882a593Smuzhiyun         if (age_ms >= vel->reset_time || age_ms < 0) {
622*4882a593Smuzhiyun             DebugAccelF("query: tracker too old (reset after %d, age is %d)\n",
623*4882a593Smuzhiyun                         vel->reset_time, age_ms);
624*4882a593Smuzhiyun             break;
625*4882a593Smuzhiyun         }
626*4882a593Smuzhiyun 
627*4882a593Smuzhiyun         /*
628*4882a593Smuzhiyun          * this heuristic avoids using the linear-motion velocity formula
629*4882a593Smuzhiyun          * in CalcTracker() on motion that isn't exactly linear. So to get
630*4882a593Smuzhiyun          * even more precision we could subdivide as a final step, so possible
631*4882a593Smuzhiyun          * non-linearities are accounted for.
632*4882a593Smuzhiyun          */
633*4882a593Smuzhiyun         dir &= tracker->dir;
634*4882a593Smuzhiyun         if (dir == 0) {         /* we've changed octant of movement (e.g. NE → NW) */
635*4882a593Smuzhiyun             DebugAccelF("query: no longer linear\n");
636*4882a593Smuzhiyun             /* instead of breaking it we might also inspect the partition after,
637*4882a593Smuzhiyun              * but actual improvement with this is probably rare. */
638*4882a593Smuzhiyun             break;
639*4882a593Smuzhiyun         }
640*4882a593Smuzhiyun 
641*4882a593Smuzhiyun         tracker_velocity = CalcTracker(tracker, cur_t) * velocity_factor;
642*4882a593Smuzhiyun 
643*4882a593Smuzhiyun         if ((initial_velocity == 0 || offset <= vel->initial_range) &&
644*4882a593Smuzhiyun             tracker_velocity != 0) {
645*4882a593Smuzhiyun             /* set initial velocity and result */
646*4882a593Smuzhiyun             result = initial_velocity = tracker_velocity;
647*4882a593Smuzhiyun             used_offset = offset;
648*4882a593Smuzhiyun         }
649*4882a593Smuzhiyun         else if (initial_velocity != 0 && tracker_velocity != 0) {
650*4882a593Smuzhiyun             velocity_diff = fabs(initial_velocity - tracker_velocity);
651*4882a593Smuzhiyun 
652*4882a593Smuzhiyun             if (velocity_diff > vel->max_diff &&
653*4882a593Smuzhiyun                 velocity_diff / (initial_velocity + tracker_velocity) >=
654*4882a593Smuzhiyun                 vel->max_rel_diff) {
655*4882a593Smuzhiyun                 /* we're not in range, quit - it won't get better. */
656*4882a593Smuzhiyun                 DebugAccelF("query: tracker too different:"
657*4882a593Smuzhiyun                             " old %2.2f initial %2.2f diff: %2.2f\n",
658*4882a593Smuzhiyun                             tracker_velocity, initial_velocity, velocity_diff);
659*4882a593Smuzhiyun                 break;
660*4882a593Smuzhiyun             }
661*4882a593Smuzhiyun             /* we're in range with the initial velocity,
662*4882a593Smuzhiyun              * so this result is likely better
663*4882a593Smuzhiyun              * (it contains more information). */
664*4882a593Smuzhiyun             result = tracker_velocity;
665*4882a593Smuzhiyun             used_offset = offset;
666*4882a593Smuzhiyun         }
667*4882a593Smuzhiyun     }
668*4882a593Smuzhiyun     if (offset == vel->num_tracker) {
669*4882a593Smuzhiyun         DebugAccelF("query: last tracker in effect\n");
670*4882a593Smuzhiyun         used_offset = vel->num_tracker - 1;
671*4882a593Smuzhiyun     }
672*4882a593Smuzhiyun     if (used_offset >= 0) {
673*4882a593Smuzhiyun #ifdef PTRACCEL_DEBUGGING
674*4882a593Smuzhiyun         MotionTracker *tracker = TRACKER(vel, used_offset);
675*4882a593Smuzhiyun 
676*4882a593Smuzhiyun         DebugAccelF("result: offset %i [dx: %f dy: %f diff: %i]\n",
677*4882a593Smuzhiyun                     used_offset, tracker->dx, tracker->dy,
678*4882a593Smuzhiyun                     cur_t - tracker->time);
679*4882a593Smuzhiyun #endif
680*4882a593Smuzhiyun     }
681*4882a593Smuzhiyun     return result;
682*4882a593Smuzhiyun }
683*4882a593Smuzhiyun 
684*4882a593Smuzhiyun #undef TRACKER_INDEX
685*4882a593Smuzhiyun #undef TRACKER
686*4882a593Smuzhiyun 
687*4882a593Smuzhiyun /**
688*4882a593Smuzhiyun  * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta).
689*4882a593Smuzhiyun  * return true if non-visible state reset is suggested
690*4882a593Smuzhiyun  */
691*4882a593Smuzhiyun BOOL
ProcessVelocityData2D(DeviceVelocityPtr vel,double dx,double dy,int time)692*4882a593Smuzhiyun ProcessVelocityData2D(DeviceVelocityPtr vel, double dx, double dy, int time)
693*4882a593Smuzhiyun {
694*4882a593Smuzhiyun     double velocity;
695*4882a593Smuzhiyun 
696*4882a593Smuzhiyun     vel->last_velocity = vel->velocity;
697*4882a593Smuzhiyun 
698*4882a593Smuzhiyun     FeedTrackers(vel, dx, dy, time);
699*4882a593Smuzhiyun 
700*4882a593Smuzhiyun     velocity = QueryTrackers(vel, time);
701*4882a593Smuzhiyun 
702*4882a593Smuzhiyun     DebugAccelF("velocity is %f\n", velocity);
703*4882a593Smuzhiyun 
704*4882a593Smuzhiyun     vel->velocity = velocity;
705*4882a593Smuzhiyun     return velocity == 0;
706*4882a593Smuzhiyun }
707*4882a593Smuzhiyun 
708*4882a593Smuzhiyun /**
709*4882a593Smuzhiyun  * this flattens significant ( > 1) mickeys a little bit for more steady
710*4882a593Smuzhiyun  * constant-velocity response
711*4882a593Smuzhiyun  */
712*4882a593Smuzhiyun static inline double
ApplySimpleSoftening(double prev_delta,double delta)713*4882a593Smuzhiyun ApplySimpleSoftening(double prev_delta, double delta)
714*4882a593Smuzhiyun {
715*4882a593Smuzhiyun     double result = delta;
716*4882a593Smuzhiyun 
717*4882a593Smuzhiyun     if (delta < -1.0 || delta > 1.0) {
718*4882a593Smuzhiyun         if (delta > prev_delta)
719*4882a593Smuzhiyun             result -= 0.5;
720*4882a593Smuzhiyun         else if (delta < prev_delta)
721*4882a593Smuzhiyun             result += 0.5;
722*4882a593Smuzhiyun     }
723*4882a593Smuzhiyun     return result;
724*4882a593Smuzhiyun }
725*4882a593Smuzhiyun 
726*4882a593Smuzhiyun /**
727*4882a593Smuzhiyun  * Soften the delta based on previous deltas stored in vel.
728*4882a593Smuzhiyun  *
729*4882a593Smuzhiyun  * @param[in,out] fdx Delta X, modified in-place.
730*4882a593Smuzhiyun  * @param[in,out] fdx Delta Y, modified in-place.
731*4882a593Smuzhiyun  */
732*4882a593Smuzhiyun static void
ApplySoftening(DeviceVelocityPtr vel,double * fdx,double * fdy)733*4882a593Smuzhiyun ApplySoftening(DeviceVelocityPtr vel, double *fdx, double *fdy)
734*4882a593Smuzhiyun {
735*4882a593Smuzhiyun     if (vel->use_softening) {
736*4882a593Smuzhiyun         *fdx = ApplySimpleSoftening(vel->last_dx, *fdx);
737*4882a593Smuzhiyun         *fdy = ApplySimpleSoftening(vel->last_dy, *fdy);
738*4882a593Smuzhiyun     }
739*4882a593Smuzhiyun }
740*4882a593Smuzhiyun 
741*4882a593Smuzhiyun static void
ApplyConstantDeceleration(DeviceVelocityPtr vel,double * fdx,double * fdy)742*4882a593Smuzhiyun ApplyConstantDeceleration(DeviceVelocityPtr vel, double *fdx, double *fdy)
743*4882a593Smuzhiyun {
744*4882a593Smuzhiyun     *fdx *= vel->const_acceleration;
745*4882a593Smuzhiyun     *fdy *= vel->const_acceleration;
746*4882a593Smuzhiyun }
747*4882a593Smuzhiyun 
748*4882a593Smuzhiyun /*
749*4882a593Smuzhiyun  * compute the acceleration for given velocity and enforce min_acceleration
750*4882a593Smuzhiyun  */
751*4882a593Smuzhiyun double
BasicComputeAcceleration(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)752*4882a593Smuzhiyun BasicComputeAcceleration(DeviceIntPtr dev,
753*4882a593Smuzhiyun                          DeviceVelocityPtr vel,
754*4882a593Smuzhiyun                          double velocity, double threshold, double acc)
755*4882a593Smuzhiyun {
756*4882a593Smuzhiyun 
757*4882a593Smuzhiyun     double result;
758*4882a593Smuzhiyun 
759*4882a593Smuzhiyun     result = vel->Profile(dev, vel, velocity, threshold, acc);
760*4882a593Smuzhiyun 
761*4882a593Smuzhiyun     /* enforce min_acceleration */
762*4882a593Smuzhiyun     if (result < vel->min_acceleration)
763*4882a593Smuzhiyun         result = vel->min_acceleration;
764*4882a593Smuzhiyun     return result;
765*4882a593Smuzhiyun }
766*4882a593Smuzhiyun 
767*4882a593Smuzhiyun /**
768*4882a593Smuzhiyun  * Compute acceleration. Takes into account averaging, nv-reset, etc.
769*4882a593Smuzhiyun  * If the velocity has changed, an average is taken of 6 velocity factors:
770*4882a593Smuzhiyun  * current velocity, last velocity and 4 times the average between the two.
771*4882a593Smuzhiyun  */
772*4882a593Smuzhiyun static double
ComputeAcceleration(DeviceIntPtr dev,DeviceVelocityPtr vel,double threshold,double acc)773*4882a593Smuzhiyun ComputeAcceleration(DeviceIntPtr dev,
774*4882a593Smuzhiyun                     DeviceVelocityPtr vel, double threshold, double acc)
775*4882a593Smuzhiyun {
776*4882a593Smuzhiyun     double result;
777*4882a593Smuzhiyun 
778*4882a593Smuzhiyun     if (vel->velocity <= 0) {
779*4882a593Smuzhiyun         DebugAccelF("profile skipped\n");
780*4882a593Smuzhiyun         /*
781*4882a593Smuzhiyun          * If we have no idea about device velocity, don't pretend it.
782*4882a593Smuzhiyun          */
783*4882a593Smuzhiyun         return 1;
784*4882a593Smuzhiyun     }
785*4882a593Smuzhiyun 
786*4882a593Smuzhiyun     if (vel->average_accel && vel->velocity != vel->last_velocity) {
787*4882a593Smuzhiyun         /* use simpson's rule to average acceleration between
788*4882a593Smuzhiyun          * current and previous velocity.
789*4882a593Smuzhiyun          * Though being the more natural choice, it causes a minor delay
790*4882a593Smuzhiyun          * in comparison, so it can be disabled. */
791*4882a593Smuzhiyun         result =
792*4882a593Smuzhiyun             BasicComputeAcceleration(dev, vel, vel->velocity, threshold, acc);
793*4882a593Smuzhiyun         result +=
794*4882a593Smuzhiyun             BasicComputeAcceleration(dev, vel, vel->last_velocity, threshold,
795*4882a593Smuzhiyun                                      acc);
796*4882a593Smuzhiyun         result +=
797*4882a593Smuzhiyun             4.0 * BasicComputeAcceleration(dev, vel,
798*4882a593Smuzhiyun                                             (vel->last_velocity +
799*4882a593Smuzhiyun                                              vel->velocity) / 2,
800*4882a593Smuzhiyun                                             threshold,
801*4882a593Smuzhiyun                                             acc);
802*4882a593Smuzhiyun         result /= 6.0;
803*4882a593Smuzhiyun         DebugAccelF("profile average [%.2f ... %.2f] is %.3f\n",
804*4882a593Smuzhiyun                     vel->velocity, vel->last_velocity, result);
805*4882a593Smuzhiyun     }
806*4882a593Smuzhiyun     else {
807*4882a593Smuzhiyun         result = BasicComputeAcceleration(dev, vel,
808*4882a593Smuzhiyun                                           vel->velocity, threshold, acc);
809*4882a593Smuzhiyun         DebugAccelF("profile sample [%.2f] is %.3f\n",
810*4882a593Smuzhiyun                     vel->velocity, result);
811*4882a593Smuzhiyun     }
812*4882a593Smuzhiyun 
813*4882a593Smuzhiyun     return result;
814*4882a593Smuzhiyun }
815*4882a593Smuzhiyun 
816*4882a593Smuzhiyun /*****************************************
817*4882a593Smuzhiyun  *  Acceleration functions and profiles
818*4882a593Smuzhiyun  ****************************************/
819*4882a593Smuzhiyun 
820*4882a593Smuzhiyun /**
821*4882a593Smuzhiyun  * Polynomial function similar previous one, but with f(1) = 1
822*4882a593Smuzhiyun  */
823*4882a593Smuzhiyun static double
PolynomialAccelerationProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double ignored,double acc)824*4882a593Smuzhiyun PolynomialAccelerationProfile(DeviceIntPtr dev,
825*4882a593Smuzhiyun                               DeviceVelocityPtr vel,
826*4882a593Smuzhiyun                               double velocity, double ignored, double acc)
827*4882a593Smuzhiyun {
828*4882a593Smuzhiyun     return pow(velocity, (acc - 1.0) * 0.5);
829*4882a593Smuzhiyun }
830*4882a593Smuzhiyun 
831*4882a593Smuzhiyun /**
832*4882a593Smuzhiyun  * returns acceleration for velocity.
833*4882a593Smuzhiyun  * This profile selects the two functions like the old scheme did
834*4882a593Smuzhiyun  */
835*4882a593Smuzhiyun static double
ClassicProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)836*4882a593Smuzhiyun ClassicProfile(DeviceIntPtr dev,
837*4882a593Smuzhiyun                DeviceVelocityPtr vel,
838*4882a593Smuzhiyun                double velocity, double threshold, double acc)
839*4882a593Smuzhiyun {
840*4882a593Smuzhiyun     if (threshold > 0) {
841*4882a593Smuzhiyun         return SimpleSmoothProfile(dev, vel, velocity, threshold, acc);
842*4882a593Smuzhiyun     }
843*4882a593Smuzhiyun     else {
844*4882a593Smuzhiyun         return PolynomialAccelerationProfile(dev, vel, velocity, 0, acc);
845*4882a593Smuzhiyun     }
846*4882a593Smuzhiyun }
847*4882a593Smuzhiyun 
848*4882a593Smuzhiyun /**
849*4882a593Smuzhiyun  * Power profile
850*4882a593Smuzhiyun  * This has a completely smooth transition curve, i.e. no jumps in the
851*4882a593Smuzhiyun  * derivatives.
852*4882a593Smuzhiyun  *
853*4882a593Smuzhiyun  * This has the expense of overall response dependency on min-acceleration.
854*4882a593Smuzhiyun  * In effect, min_acceleration mimics const_acceleration in this profile.
855*4882a593Smuzhiyun  */
856*4882a593Smuzhiyun static double
PowerProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)857*4882a593Smuzhiyun PowerProfile(DeviceIntPtr dev,
858*4882a593Smuzhiyun              DeviceVelocityPtr vel,
859*4882a593Smuzhiyun              double velocity, double threshold, double acc)
860*4882a593Smuzhiyun {
861*4882a593Smuzhiyun     double vel_dist;
862*4882a593Smuzhiyun 
863*4882a593Smuzhiyun     acc = (acc - 1.0) * 0.1 + 1.0;     /* without this, acc of 2 is unuseable */
864*4882a593Smuzhiyun 
865*4882a593Smuzhiyun     if (velocity <= threshold)
866*4882a593Smuzhiyun         return vel->min_acceleration;
867*4882a593Smuzhiyun     vel_dist = velocity - threshold;
868*4882a593Smuzhiyun     return (pow(acc, vel_dist)) * vel->min_acceleration;
869*4882a593Smuzhiyun }
870*4882a593Smuzhiyun 
871*4882a593Smuzhiyun /**
872*4882a593Smuzhiyun  * just a smooth function in [0..1] -> [0..1]
873*4882a593Smuzhiyun  *  - point symmetry at 0.5
874*4882a593Smuzhiyun  *  - f'(0) = f'(1) = 0
875*4882a593Smuzhiyun  *  - starts faster than a sinoid
876*4882a593Smuzhiyun  *  - smoothness C1 (Cinf if you dare to ignore endpoints)
877*4882a593Smuzhiyun  */
878*4882a593Smuzhiyun static inline double
CalcPenumbralGradient(double x)879*4882a593Smuzhiyun CalcPenumbralGradient(double x)
880*4882a593Smuzhiyun {
881*4882a593Smuzhiyun     x *= 2.0;
882*4882a593Smuzhiyun     x -= 1.0;
883*4882a593Smuzhiyun     return 0.5 + (x * sqrt(1.0 - x * x) + asin(x)) / M_PI;
884*4882a593Smuzhiyun }
885*4882a593Smuzhiyun 
886*4882a593Smuzhiyun /**
887*4882a593Smuzhiyun  * acceleration function similar to classic accelerated/unaccelerated,
888*4882a593Smuzhiyun  * but with smooth transition in between (and towards zero for adaptive dec.).
889*4882a593Smuzhiyun  */
890*4882a593Smuzhiyun static double
SimpleSmoothProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)891*4882a593Smuzhiyun SimpleSmoothProfile(DeviceIntPtr dev,
892*4882a593Smuzhiyun                     DeviceVelocityPtr vel,
893*4882a593Smuzhiyun                     double velocity, double threshold, double acc)
894*4882a593Smuzhiyun {
895*4882a593Smuzhiyun     if (velocity < 1.0f)
896*4882a593Smuzhiyun         return CalcPenumbralGradient(0.5 + velocity * 0.5) * 2.0f - 1.0f;
897*4882a593Smuzhiyun     if (threshold < 1.0f)
898*4882a593Smuzhiyun         threshold = 1.0f;
899*4882a593Smuzhiyun     if (velocity <= threshold)
900*4882a593Smuzhiyun         return 1;
901*4882a593Smuzhiyun     velocity /= threshold;
902*4882a593Smuzhiyun     if (velocity >= acc)
903*4882a593Smuzhiyun         return acc;
904*4882a593Smuzhiyun     else
905*4882a593Smuzhiyun         return 1.0f + (CalcPenumbralGradient(velocity / acc) * (acc - 1.0f));
906*4882a593Smuzhiyun }
907*4882a593Smuzhiyun 
908*4882a593Smuzhiyun /**
909*4882a593Smuzhiyun  * This profile uses the first half of the penumbral gradient as a start
910*4882a593Smuzhiyun  * and then scales linearly.
911*4882a593Smuzhiyun  */
912*4882a593Smuzhiyun static double
SmoothLinearProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)913*4882a593Smuzhiyun SmoothLinearProfile(DeviceIntPtr dev,
914*4882a593Smuzhiyun                     DeviceVelocityPtr vel,
915*4882a593Smuzhiyun                     double velocity, double threshold, double acc)
916*4882a593Smuzhiyun {
917*4882a593Smuzhiyun     double res, nv;
918*4882a593Smuzhiyun 
919*4882a593Smuzhiyun     if (acc > 1.0)
920*4882a593Smuzhiyun         acc -= 1.0;            /*this is so acc = 1 is no acceleration */
921*4882a593Smuzhiyun     else
922*4882a593Smuzhiyun         return 1.0;
923*4882a593Smuzhiyun 
924*4882a593Smuzhiyun     nv = (velocity - threshold) * acc * 0.5;
925*4882a593Smuzhiyun 
926*4882a593Smuzhiyun     if (nv < 0) {
927*4882a593Smuzhiyun         res = 0;
928*4882a593Smuzhiyun     }
929*4882a593Smuzhiyun     else if (nv < 2) {
930*4882a593Smuzhiyun         res = CalcPenumbralGradient(nv * 0.25) * 2.0;
931*4882a593Smuzhiyun     }
932*4882a593Smuzhiyun     else {
933*4882a593Smuzhiyun         nv -= 2.0;
934*4882a593Smuzhiyun         res = nv * 2.0 / M_PI  /* steepness of gradient at 0.5 */
935*4882a593Smuzhiyun             + 1.0;             /* gradient crosses 2|1 */
936*4882a593Smuzhiyun     }
937*4882a593Smuzhiyun     res += vel->min_acceleration;
938*4882a593Smuzhiyun     return res;
939*4882a593Smuzhiyun }
940*4882a593Smuzhiyun 
941*4882a593Smuzhiyun /**
942*4882a593Smuzhiyun  * From 0 to threshold, the response graduates smoothly from min_accel to
943*4882a593Smuzhiyun  * acceleration. Beyond threshold it is exactly the specified acceleration.
944*4882a593Smuzhiyun  */
945*4882a593Smuzhiyun static double
SmoothLimitedProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)946*4882a593Smuzhiyun SmoothLimitedProfile(DeviceIntPtr dev,
947*4882a593Smuzhiyun                      DeviceVelocityPtr vel,
948*4882a593Smuzhiyun                      double velocity, double threshold, double acc)
949*4882a593Smuzhiyun {
950*4882a593Smuzhiyun     double res;
951*4882a593Smuzhiyun 
952*4882a593Smuzhiyun     if (velocity >= threshold || threshold == 0.0)
953*4882a593Smuzhiyun         return acc;
954*4882a593Smuzhiyun 
955*4882a593Smuzhiyun     velocity /= threshold;      /* should be [0..1[ now */
956*4882a593Smuzhiyun 
957*4882a593Smuzhiyun     res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration);
958*4882a593Smuzhiyun 
959*4882a593Smuzhiyun     return vel->min_acceleration + res;
960*4882a593Smuzhiyun }
961*4882a593Smuzhiyun 
962*4882a593Smuzhiyun static double
LinearProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)963*4882a593Smuzhiyun LinearProfile(DeviceIntPtr dev,
964*4882a593Smuzhiyun               DeviceVelocityPtr vel,
965*4882a593Smuzhiyun               double velocity, double threshold, double acc)
966*4882a593Smuzhiyun {
967*4882a593Smuzhiyun     return acc * velocity;
968*4882a593Smuzhiyun }
969*4882a593Smuzhiyun 
970*4882a593Smuzhiyun static double
NoProfile(DeviceIntPtr dev,DeviceVelocityPtr vel,double velocity,double threshold,double acc)971*4882a593Smuzhiyun NoProfile(DeviceIntPtr dev,
972*4882a593Smuzhiyun           DeviceVelocityPtr vel, double velocity, double threshold, double acc)
973*4882a593Smuzhiyun {
974*4882a593Smuzhiyun     return 1.0;
975*4882a593Smuzhiyun }
976*4882a593Smuzhiyun 
977*4882a593Smuzhiyun static PointerAccelerationProfileFunc
GetAccelerationProfile(DeviceVelocityPtr vel,int profile_num)978*4882a593Smuzhiyun GetAccelerationProfile(DeviceVelocityPtr vel, int profile_num)
979*4882a593Smuzhiyun {
980*4882a593Smuzhiyun     switch (profile_num) {
981*4882a593Smuzhiyun     case AccelProfileClassic:
982*4882a593Smuzhiyun         return ClassicProfile;
983*4882a593Smuzhiyun     case AccelProfileDeviceSpecific:
984*4882a593Smuzhiyun         return vel->deviceSpecificProfile;
985*4882a593Smuzhiyun     case AccelProfilePolynomial:
986*4882a593Smuzhiyun         return PolynomialAccelerationProfile;
987*4882a593Smuzhiyun     case AccelProfileSmoothLinear:
988*4882a593Smuzhiyun         return SmoothLinearProfile;
989*4882a593Smuzhiyun     case AccelProfileSimple:
990*4882a593Smuzhiyun         return SimpleSmoothProfile;
991*4882a593Smuzhiyun     case AccelProfilePower:
992*4882a593Smuzhiyun         return PowerProfile;
993*4882a593Smuzhiyun     case AccelProfileLinear:
994*4882a593Smuzhiyun         return LinearProfile;
995*4882a593Smuzhiyun     case AccelProfileSmoothLimited:
996*4882a593Smuzhiyun         return SmoothLimitedProfile;
997*4882a593Smuzhiyun     case AccelProfileNone:
998*4882a593Smuzhiyun         return NoProfile;
999*4882a593Smuzhiyun     default:
1000*4882a593Smuzhiyun         return NULL;
1001*4882a593Smuzhiyun     }
1002*4882a593Smuzhiyun }
1003*4882a593Smuzhiyun 
1004*4882a593Smuzhiyun /**
1005*4882a593Smuzhiyun  * Set the profile by number.
1006*4882a593Smuzhiyun  * Intended to make profiles exchangeable at runtime.
1007*4882a593Smuzhiyun  * If you created a profile, give it a number here and in the header to
1008*4882a593Smuzhiyun  * make it selectable. In case some profile-specific init is needed, here
1009*4882a593Smuzhiyun  * would be a good place, since FreeVelocityData() also calls this with
1010*4882a593Smuzhiyun  * PROFILE_UNINITIALIZE.
1011*4882a593Smuzhiyun  *
1012*4882a593Smuzhiyun  * returns FALSE if profile number is unavailable, TRUE otherwise.
1013*4882a593Smuzhiyun  */
1014*4882a593Smuzhiyun int
SetAccelerationProfile(DeviceVelocityPtr vel,int profile_num)1015*4882a593Smuzhiyun SetAccelerationProfile(DeviceVelocityPtr vel, int profile_num)
1016*4882a593Smuzhiyun {
1017*4882a593Smuzhiyun     PointerAccelerationProfileFunc profile;
1018*4882a593Smuzhiyun 
1019*4882a593Smuzhiyun     profile = GetAccelerationProfile(vel, profile_num);
1020*4882a593Smuzhiyun 
1021*4882a593Smuzhiyun     if (profile == NULL && profile_num != PROFILE_UNINITIALIZE)
1022*4882a593Smuzhiyun         return FALSE;
1023*4882a593Smuzhiyun 
1024*4882a593Smuzhiyun     /* Here one could free old profile-private data */
1025*4882a593Smuzhiyun     free(vel->profile_private);
1026*4882a593Smuzhiyun     vel->profile_private = NULL;
1027*4882a593Smuzhiyun     /* Here one could init profile-private data */
1028*4882a593Smuzhiyun     vel->Profile = profile;
1029*4882a593Smuzhiyun     vel->statistics.profile_number = profile_num;
1030*4882a593Smuzhiyun     return TRUE;
1031*4882a593Smuzhiyun }
1032*4882a593Smuzhiyun 
1033*4882a593Smuzhiyun /**********************************************
1034*4882a593Smuzhiyun  * driver interaction
1035*4882a593Smuzhiyun  **********************************************/
1036*4882a593Smuzhiyun 
1037*4882a593Smuzhiyun /**
1038*4882a593Smuzhiyun  * device-specific profile
1039*4882a593Smuzhiyun  *
1040*4882a593Smuzhiyun  * The device-specific profile is intended as a hook for a driver
1041*4882a593Smuzhiyun  * which may want to provide an own acceleration profile.
1042*4882a593Smuzhiyun  * It should not rely on profile-private data, instead
1043*4882a593Smuzhiyun  * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends).
1044*4882a593Smuzhiyun  * Users may override or choose it.
1045*4882a593Smuzhiyun  */
1046*4882a593Smuzhiyun void
SetDeviceSpecificAccelerationProfile(DeviceVelocityPtr vel,PointerAccelerationProfileFunc profile)1047*4882a593Smuzhiyun SetDeviceSpecificAccelerationProfile(DeviceVelocityPtr vel,
1048*4882a593Smuzhiyun                                      PointerAccelerationProfileFunc profile)
1049*4882a593Smuzhiyun {
1050*4882a593Smuzhiyun     if (vel)
1051*4882a593Smuzhiyun         vel->deviceSpecificProfile = profile;
1052*4882a593Smuzhiyun }
1053*4882a593Smuzhiyun 
1054*4882a593Smuzhiyun /**
1055*4882a593Smuzhiyun  * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if
1056*4882a593Smuzhiyun  * the predictable acceleration scheme is not in effect.
1057*4882a593Smuzhiyun  */
1058*4882a593Smuzhiyun DeviceVelocityPtr
GetDevicePredictableAccelData(DeviceIntPtr dev)1059*4882a593Smuzhiyun GetDevicePredictableAccelData(DeviceIntPtr dev)
1060*4882a593Smuzhiyun {
1061*4882a593Smuzhiyun     BUG_RETURN_VAL(!dev, NULL);
1062*4882a593Smuzhiyun 
1063*4882a593Smuzhiyun     if (dev->valuator &&
1064*4882a593Smuzhiyun         dev->valuator->accelScheme.AccelSchemeProc ==
1065*4882a593Smuzhiyun         acceleratePointerPredictable &&
1066*4882a593Smuzhiyun         dev->valuator->accelScheme.accelData != NULL) {
1067*4882a593Smuzhiyun 
1068*4882a593Smuzhiyun         return ((PredictableAccelSchemePtr)
1069*4882a593Smuzhiyun                 dev->valuator->accelScheme.accelData)->vel;
1070*4882a593Smuzhiyun     }
1071*4882a593Smuzhiyun     return NULL;
1072*4882a593Smuzhiyun }
1073*4882a593Smuzhiyun 
1074*4882a593Smuzhiyun /********************************
1075*4882a593Smuzhiyun  *  acceleration schemes
1076*4882a593Smuzhiyun  *******************************/
1077*4882a593Smuzhiyun 
1078*4882a593Smuzhiyun /**
1079*4882a593Smuzhiyun  * Modifies valuators in-place.
1080*4882a593Smuzhiyun  * This version employs a velocity approximation algorithm to
1081*4882a593Smuzhiyun  * enable fine-grained predictable acceleration profiles.
1082*4882a593Smuzhiyun  */
1083*4882a593Smuzhiyun void
acceleratePointerPredictable(DeviceIntPtr dev,ValuatorMask * val,CARD32 evtime)1084*4882a593Smuzhiyun acceleratePointerPredictable(DeviceIntPtr dev, ValuatorMask *val, CARD32 evtime)
1085*4882a593Smuzhiyun {
1086*4882a593Smuzhiyun     double dx = 0, dy = 0;
1087*4882a593Smuzhiyun     DeviceVelocityPtr velocitydata = GetDevicePredictableAccelData(dev);
1088*4882a593Smuzhiyun     Bool soften = TRUE;
1089*4882a593Smuzhiyun 
1090*4882a593Smuzhiyun     if (valuator_mask_num_valuators(val) == 0 || !velocitydata)
1091*4882a593Smuzhiyun         return;
1092*4882a593Smuzhiyun 
1093*4882a593Smuzhiyun     if (velocitydata->statistics.profile_number == AccelProfileNone &&
1094*4882a593Smuzhiyun         velocitydata->const_acceleration == 1.0) {
1095*4882a593Smuzhiyun         return;                 /*we're inactive anyway, so skip the whole thing. */
1096*4882a593Smuzhiyun     }
1097*4882a593Smuzhiyun 
1098*4882a593Smuzhiyun     if (valuator_mask_isset(val, 0)) {
1099*4882a593Smuzhiyun         dx = valuator_mask_get_double(val, 0);
1100*4882a593Smuzhiyun     }
1101*4882a593Smuzhiyun 
1102*4882a593Smuzhiyun     if (valuator_mask_isset(val, 1)) {
1103*4882a593Smuzhiyun         dy = valuator_mask_get_double(val, 1);
1104*4882a593Smuzhiyun     }
1105*4882a593Smuzhiyun 
1106*4882a593Smuzhiyun     if (dx != 0.0 || dy != 0.0) {
1107*4882a593Smuzhiyun         /* reset non-visible state? */
1108*4882a593Smuzhiyun         if (ProcessVelocityData2D(velocitydata, dx, dy, evtime)) {
1109*4882a593Smuzhiyun             soften = FALSE;
1110*4882a593Smuzhiyun         }
1111*4882a593Smuzhiyun 
1112*4882a593Smuzhiyun         if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
1113*4882a593Smuzhiyun             double mult;
1114*4882a593Smuzhiyun 
1115*4882a593Smuzhiyun             /* invoke acceleration profile to determine acceleration */
1116*4882a593Smuzhiyun             mult = ComputeAcceleration(dev, velocitydata,
1117*4882a593Smuzhiyun                                        dev->ptrfeed->ctrl.threshold,
1118*4882a593Smuzhiyun                                        (double) dev->ptrfeed->ctrl.num /
1119*4882a593Smuzhiyun                                        (double) dev->ptrfeed->ctrl.den);
1120*4882a593Smuzhiyun 
1121*4882a593Smuzhiyun             DebugAccelF("mult is %f\n", mult);
1122*4882a593Smuzhiyun             if (mult != 1.0 || velocitydata->const_acceleration != 1.0) {
1123*4882a593Smuzhiyun                 if (mult > 1.0 && soften)
1124*4882a593Smuzhiyun                     ApplySoftening(velocitydata, &dx, &dy);
1125*4882a593Smuzhiyun                 ApplyConstantDeceleration(velocitydata, &dx, &dy);
1126*4882a593Smuzhiyun 
1127*4882a593Smuzhiyun                 if (dx != 0.0)
1128*4882a593Smuzhiyun                     valuator_mask_set_double(val, 0, mult * dx);
1129*4882a593Smuzhiyun                 if (dy != 0.0)
1130*4882a593Smuzhiyun                     valuator_mask_set_double(val, 1, mult * dy);
1131*4882a593Smuzhiyun                 DebugAccelF("delta x:%.3f y:%.3f\n", mult * dx, mult * dy);
1132*4882a593Smuzhiyun             }
1133*4882a593Smuzhiyun         }
1134*4882a593Smuzhiyun     }
1135*4882a593Smuzhiyun     /* remember last motion delta (for softening/slow movement treatment) */
1136*4882a593Smuzhiyun     velocitydata->last_dx = dx;
1137*4882a593Smuzhiyun     velocitydata->last_dy = dy;
1138*4882a593Smuzhiyun }
1139*4882a593Smuzhiyun 
1140*4882a593Smuzhiyun /**
1141*4882a593Smuzhiyun  * Originally a part of xf86PostMotionEvent; modifies valuators
1142*4882a593Smuzhiyun  * in-place. Retained mostly for embedded scenarios.
1143*4882a593Smuzhiyun  */
1144*4882a593Smuzhiyun void
acceleratePointerLightweight(DeviceIntPtr dev,ValuatorMask * val,CARD32 ignored)1145*4882a593Smuzhiyun acceleratePointerLightweight(DeviceIntPtr dev,
1146*4882a593Smuzhiyun                              ValuatorMask *val, CARD32 ignored)
1147*4882a593Smuzhiyun {
1148*4882a593Smuzhiyun     double mult = 0.0, tmpf;
1149*4882a593Smuzhiyun     double dx = 0.0, dy = 0.0;
1150*4882a593Smuzhiyun 
1151*4882a593Smuzhiyun     if (valuator_mask_isset(val, 0)) {
1152*4882a593Smuzhiyun         dx = valuator_mask_get(val, 0);
1153*4882a593Smuzhiyun     }
1154*4882a593Smuzhiyun 
1155*4882a593Smuzhiyun     if (valuator_mask_isset(val, 1)) {
1156*4882a593Smuzhiyun         dy = valuator_mask_get(val, 1);
1157*4882a593Smuzhiyun     }
1158*4882a593Smuzhiyun 
1159*4882a593Smuzhiyun     if (valuator_mask_num_valuators(val) == 0)
1160*4882a593Smuzhiyun         return;
1161*4882a593Smuzhiyun 
1162*4882a593Smuzhiyun     if (dev->ptrfeed && dev->ptrfeed->ctrl.num) {
1163*4882a593Smuzhiyun         /* modeled from xf86Events.c */
1164*4882a593Smuzhiyun         if (dev->ptrfeed->ctrl.threshold) {
1165*4882a593Smuzhiyun             if ((fabs(dx) + fabs(dy)) >= dev->ptrfeed->ctrl.threshold) {
1166*4882a593Smuzhiyun                 if (dx != 0.0) {
1167*4882a593Smuzhiyun                     tmpf = (dx * (double) (dev->ptrfeed->ctrl.num)) /
1168*4882a593Smuzhiyun                         (double) (dev->ptrfeed->ctrl.den);
1169*4882a593Smuzhiyun                     valuator_mask_set_double(val, 0, tmpf);
1170*4882a593Smuzhiyun                 }
1171*4882a593Smuzhiyun 
1172*4882a593Smuzhiyun                 if (dy != 0.0) {
1173*4882a593Smuzhiyun                     tmpf = (dy * (double) (dev->ptrfeed->ctrl.num)) /
1174*4882a593Smuzhiyun                         (double) (dev->ptrfeed->ctrl.den);
1175*4882a593Smuzhiyun                     valuator_mask_set_double(val, 1, tmpf);
1176*4882a593Smuzhiyun                 }
1177*4882a593Smuzhiyun             }
1178*4882a593Smuzhiyun         }
1179*4882a593Smuzhiyun         else {
1180*4882a593Smuzhiyun             mult = pow(dx * dx + dy * dy,
1181*4882a593Smuzhiyun                        ((double) (dev->ptrfeed->ctrl.num) /
1182*4882a593Smuzhiyun                         (double) (dev->ptrfeed->ctrl.den) - 1.0) / 2.0) / 2.0;
1183*4882a593Smuzhiyun             if (dx != 0.0)
1184*4882a593Smuzhiyun                 valuator_mask_set_double(val, 0, mult * dx);
1185*4882a593Smuzhiyun             if (dy != 0.0)
1186*4882a593Smuzhiyun                 valuator_mask_set_double(val, 1, mult * dy);
1187*4882a593Smuzhiyun         }
1188*4882a593Smuzhiyun     }
1189*4882a593Smuzhiyun }
1190