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