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