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