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