ptrveloc.c revision 9ace9065
14642e01fSmrg/* 24642e01fSmrg * 36747b715Smrg * Copyright © 2006-2009 Simon Thum simon dot thum at gmx dot de 44642e01fSmrg * 54642e01fSmrg * Permission is hereby granted, free of charge, to any person obtaining a 64642e01fSmrg * copy of this software and associated documentation files (the "Software"), 74642e01fSmrg * to deal in the Software without restriction, including without limitation 84642e01fSmrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 94642e01fSmrg * and/or sell copies of the Software, and to permit persons to whom the 104642e01fSmrg * Software is furnished to do so, subject to the following conditions: 114642e01fSmrg * 124642e01fSmrg * The above copyright notice and this permission notice (including the next 134642e01fSmrg * paragraph) shall be included in all copies or substantial portions of the 144642e01fSmrg * Software. 154642e01fSmrg * 164642e01fSmrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 174642e01fSmrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 184642e01fSmrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 194642e01fSmrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 204642e01fSmrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 214642e01fSmrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 224642e01fSmrg * DEALINGS IN THE SOFTWARE. 234642e01fSmrg */ 244642e01fSmrg 254642e01fSmrg#ifdef HAVE_DIX_CONFIG_H 264642e01fSmrg#include <dix-config.h> 274642e01fSmrg#endif 284642e01fSmrg 294642e01fSmrg#include <math.h> 304642e01fSmrg#include <ptrveloc.h> 316747b715Smrg#include <exevents.h> 326747b715Smrg#include <X11/Xatom.h> 336747b715Smrg 346747b715Smrg#include <xserver-properties.h> 354642e01fSmrg 364642e01fSmrg/***************************************************************************** 374642e01fSmrg * Predictable pointer acceleration 384642e01fSmrg * 396747b715Smrg * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de) 404642e01fSmrg * 414642e01fSmrg * Serves 3 complementary functions: 424642e01fSmrg * 1) provide a sophisticated ballistic velocity estimate to improve 434642e01fSmrg * the relation between velocity (of the device) and acceleration 444642e01fSmrg * 2) make arbitrary acceleration profiles possible 454642e01fSmrg * 3) decelerate by two means (constant and adaptive) if enabled 464642e01fSmrg * 474642e01fSmrg * Important concepts are the 484642e01fSmrg * 494642e01fSmrg * - Scheme 504642e01fSmrg * which selects the basic algorithm 514642e01fSmrg * (see devices.c/InitPointerAccelerationScheme) 524642e01fSmrg * - Profile 534642e01fSmrg * which returns an acceleration 544642e01fSmrg * for a given velocity 554642e01fSmrg * 566747b715Smrg * The profile can be selected by the user at runtime. 576747b715Smrg * The classic profile is intended to cleanly perform old-style 584642e01fSmrg * function selection (threshold =/!= 0) 594642e01fSmrg * 604642e01fSmrg ****************************************************************************/ 614642e01fSmrg 624642e01fSmrg/* fwds */ 634642e01fSmrgint 646747b715SmrgSetAccelerationProfile(DeviceVelocityPtr vel, int profile_num); 654642e01fSmrgstatic float 666747b715SmrgSimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, float velocity, 674642e01fSmrg float threshold, float acc); 686747b715Smrgstatic PointerAccelerationProfileFunc 696747b715SmrgGetAccelerationProfile(DeviceVelocityPtr vel, int profile_num); 704642e01fSmrg 714642e01fSmrg/*#define PTRACCEL_DEBUGGING*/ 724642e01fSmrg 734642e01fSmrg#ifdef PTRACCEL_DEBUGGING 744642e01fSmrg#define DebugAccelF ErrorF 754642e01fSmrg#else 764642e01fSmrg#define DebugAccelF(...) /* */ 774642e01fSmrg#endif 784642e01fSmrg 794642e01fSmrg/******************************** 806747b715Smrg * Init/Uninit 814642e01fSmrg *******************************/ 824642e01fSmrg 836747b715Smrg/* some int which is not a profile number */ 846747b715Smrg#define PROFILE_UNINITIALIZE (-100) 856747b715Smrg 866747b715Smrg 874642e01fSmrg/** 884642e01fSmrg * Init struct so it should match the average case 894642e01fSmrg */ 904642e01fSmrgvoid 916747b715SmrgInitVelocityData(DeviceVelocityPtr vel) 924642e01fSmrg{ 936747b715Smrg memset(vel, 0, sizeof(DeviceVelocityRec)); 946747b715Smrg 956747b715Smrg vel->corr_mul = 10.0; /* dots per 10 milisecond should be usable */ 966747b715Smrg vel->const_acceleration = 1.0; /* no acceleration/deceleration */ 976747b715Smrg vel->reset_time = 300; 986747b715Smrg vel->use_softening = 1; 996747b715Smrg vel->min_acceleration = 1.0; /* don't decelerate */ 1006747b715Smrg vel->max_rel_diff = 0.2; 1016747b715Smrg vel->max_diff = 1.0; 1026747b715Smrg vel->initial_range = 2; 1036747b715Smrg vel->average_accel = TRUE; 1046747b715Smrg SetAccelerationProfile(vel, AccelProfileClassic); 1056747b715Smrg InitTrackers(vel, 16); 1064642e01fSmrg} 1074642e01fSmrg 1084642e01fSmrg 1094642e01fSmrg/** 1104642e01fSmrg * Clean up 1114642e01fSmrg */ 1126747b715Smrgvoid 1136747b715SmrgFreeVelocityData(DeviceVelocityPtr vel){ 1146747b715Smrg free(vel->tracker); 1156747b715Smrg SetAccelerationProfile(vel, PROFILE_UNINITIALIZE); 1164642e01fSmrg} 1174642e01fSmrg 1184642e01fSmrg 1194642e01fSmrg/* 1204642e01fSmrg * dix uninit helper, called through scheme 1214642e01fSmrg */ 1224642e01fSmrgvoid 1236747b715SmrgAccelerationDefaultCleanup(DeviceIntPtr dev) 1244642e01fSmrg{ 1254642e01fSmrg /*sanity check*/ 1266747b715Smrg if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable 1276747b715Smrg && dev->valuator->accelScheme.accelData != NULL){ 1286747b715Smrg dev->valuator->accelScheme.AccelSchemeProc = NULL; 1296747b715Smrg FreeVelocityData(dev->valuator->accelScheme.accelData); 1306747b715Smrg free(dev->valuator->accelScheme.accelData); 1316747b715Smrg dev->valuator->accelScheme.accelData = NULL; 1326747b715Smrg DeletePredictableAccelerationProperties(dev); 1334642e01fSmrg } 1344642e01fSmrg} 1354642e01fSmrg 1366747b715Smrg 1376747b715Smrg/************************* 1386747b715Smrg * Input property support 1396747b715Smrg ************************/ 1404642e01fSmrg 1414642e01fSmrg/** 1426747b715Smrg * choose profile 1436747b715Smrg */ 1446747b715Smrgstatic int 1456747b715SmrgAccelSetProfileProperty(DeviceIntPtr dev, Atom atom, 1466747b715Smrg XIPropertyValuePtr val, BOOL checkOnly) 1474642e01fSmrg{ 1486747b715Smrg DeviceVelocityPtr vel; 1496747b715Smrg int profile, *ptr = &profile; 1506747b715Smrg int rc; 1516747b715Smrg int nelem = 1; 1526747b715Smrg 1536747b715Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER)) 1546747b715Smrg return Success; 1556747b715Smrg 1566747b715Smrg vel = GetDevicePredictableAccelData(dev); 1576747b715Smrg if (!vel) 1586747b715Smrg return BadValue; 1596747b715Smrg rc = XIPropToInt(val, &nelem, &ptr); 1606747b715Smrg 1616747b715Smrg if(checkOnly) 1626747b715Smrg { 1636747b715Smrg if (rc) 1646747b715Smrg return rc; 1656747b715Smrg 1666747b715Smrg if (GetAccelerationProfile(vel, profile) == NULL) 1676747b715Smrg return BadValue; 1686747b715Smrg } else 1696747b715Smrg SetAccelerationProfile(vel, profile); 1706747b715Smrg 1716747b715Smrg return Success; 1724642e01fSmrg} 1734642e01fSmrg 1746747b715Smrgstatic long 1756747b715SmrgAccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 1764642e01fSmrg{ 1776747b715Smrg int profile = vel->statistics.profile_number; 1786747b715Smrg Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); 1794642e01fSmrg 1806747b715Smrg XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32, 1816747b715Smrg PropModeReplace, 1, &profile, FALSE); 1826747b715Smrg XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE); 1836747b715Smrg return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL); 1844642e01fSmrg} 1854642e01fSmrg 1866747b715Smrg/** 1876747b715Smrg * constant deceleration 1886747b715Smrg */ 1896747b715Smrgstatic int 1906747b715SmrgAccelSetDecelProperty(DeviceIntPtr dev, Atom atom, 1916747b715Smrg XIPropertyValuePtr val, BOOL checkOnly) 1924642e01fSmrg{ 1936747b715Smrg DeviceVelocityPtr vel; 1946747b715Smrg float v, *ptr = &v; 1956747b715Smrg int rc; 1966747b715Smrg int nelem = 1; 1976747b715Smrg 1986747b715Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION)) 1996747b715Smrg return Success; 2006747b715Smrg 2016747b715Smrg vel = GetDevicePredictableAccelData(dev); 2026747b715Smrg if (!vel) 2036747b715Smrg return BadValue; 2046747b715Smrg rc = XIPropToFloat(val, &nelem, &ptr); 2056747b715Smrg 2066747b715Smrg if(checkOnly) 2076747b715Smrg { 2086747b715Smrg if (rc) 2096747b715Smrg return rc; 2106747b715Smrg return (v >= 1.0f) ? Success : BadValue; 2114642e01fSmrg } 2126747b715Smrg 2136747b715Smrg if(v >= 1.0f) 2146747b715Smrg vel->const_acceleration = 1/v; 2156747b715Smrg 2166747b715Smrg return Success; 2176747b715Smrg} 2186747b715Smrg 2196747b715Smrgstatic long 2206747b715SmrgAccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 2216747b715Smrg{ 2226747b715Smrg float fval = 1.0/vel->const_acceleration; 2236747b715Smrg Atom prop_const_decel = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION); 2246747b715Smrg XIChangeDeviceProperty(dev, prop_const_decel, 2256747b715Smrg XIGetKnownProperty(XATOM_FLOAT), 32, 2266747b715Smrg PropModeReplace, 1, &fval, FALSE); 2276747b715Smrg XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE); 2286747b715Smrg return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL); 2294642e01fSmrg} 2304642e01fSmrg 2314642e01fSmrg 2324642e01fSmrg/** 2336747b715Smrg * adaptive deceleration 2344642e01fSmrg */ 2356747b715Smrgstatic int 2366747b715SmrgAccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom, 2376747b715Smrg XIPropertyValuePtr val, BOOL checkOnly) 2384642e01fSmrg{ 2396747b715Smrg DeviceVelocityPtr veloc; 2406747b715Smrg float v, *ptr = &v; 2416747b715Smrg int rc; 2426747b715Smrg int nelem = 1; 2436747b715Smrg 2446747b715Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION)) 2456747b715Smrg return Success; 2466747b715Smrg 2476747b715Smrg veloc = GetDevicePredictableAccelData(dev); 2486747b715Smrg if (!veloc) 2496747b715Smrg return BadValue; 2506747b715Smrg rc = XIPropToFloat(val, &nelem, &ptr); 2516747b715Smrg 2526747b715Smrg if(checkOnly) 2536747b715Smrg { 2546747b715Smrg if (rc) 2556747b715Smrg return rc; 2566747b715Smrg return (v >= 1.0f) ? Success : BadValue; 2574642e01fSmrg } 2586747b715Smrg 2596747b715Smrg if(v >= 1.0f) 2606747b715Smrg veloc->min_acceleration = 1/v; 2616747b715Smrg 2626747b715Smrg return Success; 2634642e01fSmrg} 2644642e01fSmrg 2656747b715Smrgstatic long 2666747b715SmrgAccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 2676747b715Smrg{ 2686747b715Smrg float fval = 1.0/vel->min_acceleration; 2696747b715Smrg Atom prop_adapt_decel = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION); 2704642e01fSmrg 2716747b715Smrg XIChangeDeviceProperty(dev, prop_adapt_decel, XIGetKnownProperty(XATOM_FLOAT), 32, 2726747b715Smrg PropModeReplace, 1, &fval, FALSE); 2736747b715Smrg XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE); 2746747b715Smrg return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL, NULL); 2756747b715Smrg} 2766747b715Smrg 2776747b715Smrg 2786747b715Smrg/** 2796747b715Smrg * velocity scaling 2806747b715Smrg */ 2816747b715Smrgstatic int 2826747b715SmrgAccelSetScaleProperty(DeviceIntPtr dev, Atom atom, 2836747b715Smrg XIPropertyValuePtr val, BOOL checkOnly) 2844642e01fSmrg{ 2856747b715Smrg DeviceVelocityPtr vel; 2866747b715Smrg float v, *ptr = &v; 2876747b715Smrg int rc; 2886747b715Smrg int nelem = 1; 2896747b715Smrg 2906747b715Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING)) 2916747b715Smrg return Success; 2924642e01fSmrg 2936747b715Smrg vel = GetDevicePredictableAccelData(dev); 2946747b715Smrg if (!vel) 2956747b715Smrg return BadValue; 2966747b715Smrg rc = XIPropToFloat(val, &nelem, &ptr); 2976747b715Smrg 2986747b715Smrg if (checkOnly) 2996747b715Smrg { 3006747b715Smrg if (rc) 3016747b715Smrg return rc; 3026747b715Smrg 3036747b715Smrg return (v > 0) ? Success : BadValue; 3044642e01fSmrg } 3056747b715Smrg 3066747b715Smrg if(v > 0) 3076747b715Smrg vel->corr_mul = v; 3086747b715Smrg 3096747b715Smrg return Success; 3104642e01fSmrg} 3114642e01fSmrg 3126747b715Smrgstatic long 3136747b715SmrgAccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 3146747b715Smrg{ 3156747b715Smrg float fval = vel->corr_mul; 3166747b715Smrg Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING); 3174642e01fSmrg 3186747b715Smrg XIChangeDeviceProperty(dev, prop_velo_scale, XIGetKnownProperty(XATOM_FLOAT), 32, 3196747b715Smrg PropModeReplace, 1, &fval, FALSE); 3206747b715Smrg XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE); 3216747b715Smrg return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL); 3224642e01fSmrg} 3234642e01fSmrg 3246747b715SmrgBOOL 3256747b715SmrgInitializePredictableAccelerationProperties(DeviceIntPtr dev) 3264642e01fSmrg{ 3276747b715Smrg DeviceVelocityPtr vel = GetDevicePredictableAccelData(dev); 3284642e01fSmrg 3296747b715Smrg if(!vel) 3306747b715Smrg return FALSE; 3314642e01fSmrg 3326747b715Smrg vel->prop_handlers[0] = AccelInitProfileProperty(dev, vel); 3336747b715Smrg vel->prop_handlers[1] = AccelInitDecelProperty(dev, vel); 3346747b715Smrg vel->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel); 3356747b715Smrg vel->prop_handlers[3] = AccelInitScaleProperty(dev, vel); 3364642e01fSmrg 3376747b715Smrg return TRUE; 3386747b715Smrg} 3394642e01fSmrg 3406747b715SmrgBOOL 3416747b715SmrgDeletePredictableAccelerationProperties(DeviceIntPtr dev) 3426747b715Smrg{ 3436747b715Smrg DeviceVelocityPtr vel; 3446747b715Smrg Atom prop; 3456747b715Smrg int i; 3466747b715Smrg 3476747b715Smrg prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING); 3486747b715Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 3496747b715Smrg prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION); 3506747b715Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 3516747b715Smrg prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION); 3526747b715Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 3536747b715Smrg prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); 3546747b715Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 3556747b715Smrg 3566747b715Smrg vel = GetDevicePredictableAccelData(dev); 3576747b715Smrg for (i = 0; vel && i < NPROPS_PREDICTABLE_ACCEL; i++) 3586747b715Smrg if (vel->prop_handlers[i]) 3596747b715Smrg XIUnregisterPropertyHandler(dev, vel->prop_handlers[i]); 3604642e01fSmrg 3616747b715Smrg return TRUE; 3624642e01fSmrg} 3634642e01fSmrg 3646747b715Smrg/********************* 3656747b715Smrg * Tracking logic 3666747b715Smrg ********************/ 3674642e01fSmrg 3686747b715Smrgvoid 3696747b715SmrgInitTrackers(DeviceVelocityPtr vel, int ntracker) 3706747b715Smrg{ 3716747b715Smrg if(ntracker < 1){ 3726747b715Smrg ErrorF("(dix ptracc) invalid number of trackers\n"); 3736747b715Smrg return; 3744642e01fSmrg } 3756747b715Smrg free(vel->tracker); 3766747b715Smrg vel->tracker = (MotionTrackerPtr)malloc(ntracker * sizeof(MotionTracker)); 3776747b715Smrg memset(vel->tracker, 0, ntracker * sizeof(MotionTracker)); 3786747b715Smrg vel->num_tracker = ntracker; 3794642e01fSmrg} 3804642e01fSmrg 3814642e01fSmrg/** 3826747b715Smrg * return a bit field of possible directions. 3836747b715Smrg * 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess. 3846747b715Smrg * There's no reason against widening to more precise directions (<45 degrees), 3856747b715Smrg * should it not perform well. All this is needed for is sort out non-linear 3866747b715Smrg * motion, so precision isn't paramount. However, one should not flag direction 3876747b715Smrg * too narrow, since it would then cut the linear segment to zero size way too 3886747b715Smrg * often. 3894642e01fSmrg */ 3906747b715Smrgstatic int 3916747b715SmrgDoGetDirection(int dx, int dy){ 3926747b715Smrg float r; 3936747b715Smrg int i1, i2; 3946747b715Smrg /* on insignificant mickeys, flag 135 degrees */ 3959ace9065Smrg if(abs(dx) < 2 && abs(dy) < 2){ 3966747b715Smrg /* first check diagonal cases */ 3976747b715Smrg if(dx > 0 && dy > 0) 3986747b715Smrg return 4+8+16; 3996747b715Smrg if(dx > 0 && dy < 0) 4006747b715Smrg return 1+2+4; 4016747b715Smrg if(dx < 0 && dy < 0) 4026747b715Smrg return 1+128+64; 4036747b715Smrg if(dx < 0 && dy > 0) 4046747b715Smrg return 16+32+64; 4056747b715Smrg /* check axis-aligned directions */ 4066747b715Smrg if(dx > 0) 4076747b715Smrg return 2+4+8; /*E*/ 4086747b715Smrg if(dx < 0) 4096747b715Smrg return 128+64+32; /*W*/ 4106747b715Smrg if(dy > 0) 4116747b715Smrg return 32+16+8; /*S*/ 4126747b715Smrg if(dy < 0) 4136747b715Smrg return 128+1+2; /*N*/ 4146747b715Smrg return 255; /* shouldn't happen */ 4154642e01fSmrg } 4166747b715Smrg /* else, compute angle and set appropriate flags */ 4176747b715Smrg#ifdef _ISOC99_SOURCE 4186747b715Smrg r = atan2f(dy, dx); 4196747b715Smrg#else 4206747b715Smrg r = atan2(dy, dx); 4216747b715Smrg#endif 4226747b715Smrg /* find direction. We avoid r to become negative, 4236747b715Smrg * since C has no well-defined modulo for such cases. */ 4246747b715Smrg r = (r+(M_PI*2.5))/(M_PI/4); 4256747b715Smrg /* this intends to flag 2 directions (90 degrees), 4266747b715Smrg * except on very well-aligned mickeys. */ 4276747b715Smrg i1 = (int)(r+0.1) % 8; 4286747b715Smrg i2 = (int)(r+0.9) % 8; 4296747b715Smrg if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7) 4306747b715Smrg return 255; /* shouldn't happen */ 4316747b715Smrg return 1 << i1 | 1 << i2; 4326747b715Smrg} 4334642e01fSmrg 4346747b715Smrg#define DIRECTION_CACHE_RANGE 5 4356747b715Smrg#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1) 4366747b715Smrg 4376747b715Smrg/* cache DoGetDirection(). */ 4386747b715Smrgstatic int 4396747b715SmrgGetDirection(int dx, int dy){ 4406747b715Smrg static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE]; 4416747b715Smrg int i; 4426747b715Smrg if (abs(dx) <= DIRECTION_CACHE_RANGE && 4436747b715Smrg abs(dy) <= DIRECTION_CACHE_RANGE) { 4446747b715Smrg /* cacheable */ 4456747b715Smrg i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy]; 4466747b715Smrg if(i != 0){ 4476747b715Smrg return i; 4486747b715Smrg }else{ 4496747b715Smrg i = DoGetDirection(dx, dy); 4506747b715Smrg cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i; 4516747b715Smrg return i; 4526747b715Smrg } 4536747b715Smrg }else{ 4546747b715Smrg /* non-cacheable */ 4556747b715Smrg return DoGetDirection(dx, dy); 4564642e01fSmrg } 4576747b715Smrg} 4584642e01fSmrg 4596747b715Smrg#undef DIRECTION_CACHE_RANGE 4606747b715Smrg#undef DIRECTION_CACHE_SIZE 4614642e01fSmrg 4624642e01fSmrg 4636747b715Smrg/* convert offset (age) to array index */ 4646747b715Smrg#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker) 4656747b715Smrg 4666747b715Smrgstatic inline void 4676747b715SmrgFeedTrackers(DeviceVelocityPtr vel, int dx, int dy, int cur_t) 4686747b715Smrg{ 4696747b715Smrg int n; 4706747b715Smrg for(n = 0; n < vel->num_tracker; n++){ 4716747b715Smrg vel->tracker[n].dx += dx; 4726747b715Smrg vel->tracker[n].dy += dy; 4734642e01fSmrg } 4746747b715Smrg n = (vel->cur_tracker + 1) % vel->num_tracker; 4756747b715Smrg vel->tracker[n].dx = 0; 4766747b715Smrg vel->tracker[n].dy = 0; 4776747b715Smrg vel->tracker[n].time = cur_t; 4786747b715Smrg vel->tracker[n].dir = GetDirection(dx, dy); 4796747b715Smrg DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n", 4806747b715Smrg dx, dy, vel->tracker[n].dir, 4816747b715Smrg cur_t - vel->tracker[vel->cur_tracker].time); 4826747b715Smrg vel->cur_tracker = n; 4836747b715Smrg} 4846747b715Smrg 4856747b715Smrg/** 4866747b715Smrg * calc velocity for given tracker, with 4876747b715Smrg * velocity scaling. 4886747b715Smrg * This assumes linear motion. 4896747b715Smrg */ 4906747b715Smrgstatic float 4916747b715SmrgCalcTracker(DeviceVelocityPtr vel, int offset, int cur_t){ 4926747b715Smrg int index = TRACKER_INDEX(vel, offset); 4936747b715Smrg float dist = sqrt( vel->tracker[index].dx * vel->tracker[index].dx 4946747b715Smrg + vel->tracker[index].dy * vel->tracker[index].dy); 4956747b715Smrg int dtime = cur_t - vel->tracker[index].time; 4966747b715Smrg if(dtime > 0) 4976747b715Smrg return dist / dtime; 4986747b715Smrg else 4996747b715Smrg return 0;/* synonymous for NaN, since we're not C99 */ 5006747b715Smrg} 5016747b715Smrg 5026747b715Smrg/* find the most plausible velocity. That is, the most distant 5036747b715Smrg * (in time) tracker which isn't too old, beyond a linear partition, 5046747b715Smrg * or simply too much off initial velocity. 5056747b715Smrg * 5066747b715Smrg * May return 0. 5076747b715Smrg */ 5086747b715Smrgstatic float 5096747b715SmrgQueryTrackers(DeviceVelocityPtr vel, int cur_t){ 5106747b715Smrg int n, offset, dir = 255, i = -1, age_ms; 5116747b715Smrg /* initial velocity: a low-offset, valid velocity */ 5126747b715Smrg float iveloc = 0, res = 0, tmp, vdiff; 5136747b715Smrg float vfac = vel->corr_mul * vel->const_acceleration; /* premultiply */ 5146747b715Smrg /* loop from current to older data */ 5156747b715Smrg for(offset = 1; offset < vel->num_tracker; offset++){ 5166747b715Smrg n = TRACKER_INDEX(vel, offset); 5176747b715Smrg 5186747b715Smrg age_ms = cur_t - vel->tracker[n].time; 5196747b715Smrg 5206747b715Smrg /* bail out if data is too old and protect from overrun */ 5216747b715Smrg if (age_ms >= vel->reset_time || age_ms < 0) { 5226747b715Smrg DebugAccelF("(dix prtacc) query: tracker too old\n"); 5236747b715Smrg break; 5246747b715Smrg } 5254642e01fSmrg 5264642e01fSmrg /* 5276747b715Smrg * this heuristic avoids using the linear-motion velocity formula 5286747b715Smrg * in CalcTracker() on motion that isn't exactly linear. So to get 5296747b715Smrg * even more precision we could subdivide as a final step, so possible 5306747b715Smrg * non-linearities are accounted for. 5314642e01fSmrg */ 5326747b715Smrg dir &= vel->tracker[n].dir; 5336747b715Smrg if(dir == 0){ 5346747b715Smrg DebugAccelF("(dix prtacc) query: no longer linear\n"); 5356747b715Smrg /* instead of breaking it we might also inspect the partition after, 5366747b715Smrg * but actual improvement with this is probably rare. */ 5376747b715Smrg break; 5386747b715Smrg } 5396747b715Smrg 5406747b715Smrg tmp = CalcTracker(vel, offset, cur_t) * vfac; 5416747b715Smrg 5426747b715Smrg if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) { 5436747b715Smrg /* set initial velocity and result */ 5446747b715Smrg res = iveloc = tmp; 5456747b715Smrg i = offset; 5466747b715Smrg } else if (iveloc != 0 && tmp != 0) { 5476747b715Smrg vdiff = fabs(iveloc - tmp); 5486747b715Smrg if (vdiff <= vel->max_diff || 5496747b715Smrg vdiff/(iveloc + tmp) < vel->max_rel_diff) { 5506747b715Smrg /* we're in range with the initial velocity, 5516747b715Smrg * so this result is likely better 5526747b715Smrg * (it contains more information). */ 5536747b715Smrg res = tmp; 5546747b715Smrg i = offset; 5556747b715Smrg }else{ 5566747b715Smrg /* we're not in range, quit - it won't get better. */ 5576747b715Smrg DebugAccelF("(dix prtacc) query: tracker too different:" 5586747b715Smrg " old %2.2f initial %2.2f diff: %2.2f\n", 5596747b715Smrg tmp, iveloc, vdiff); 5606747b715Smrg break; 5616747b715Smrg } 5626747b715Smrg } 5636747b715Smrg } 5646747b715Smrg if(offset == vel->num_tracker){ 5656747b715Smrg DebugAccelF("(dix prtacc) query: last tracker in effect\n"); 5666747b715Smrg i = vel->num_tracker-1; 5676747b715Smrg } 5686747b715Smrg if(i>=0){ 5696747b715Smrg n = TRACKER_INDEX(vel, i); 5706747b715Smrg DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n", 5716747b715Smrg i, 5726747b715Smrg vel->tracker[n].dx, 5736747b715Smrg vel->tracker[n].dy, 5746747b715Smrg cur_t - vel->tracker[n].time); 5754642e01fSmrg } 5766747b715Smrg return res; 5776747b715Smrg} 5784642e01fSmrg 5796747b715Smrg#undef TRACKER_INDEX 5804642e01fSmrg 5816747b715Smrg/** 5826747b715Smrg * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta). 5836747b715Smrg * return true if non-visible state reset is suggested 5846747b715Smrg */ 5856747b715Smrgshort 5866747b715SmrgProcessVelocityData2D( 5876747b715Smrg DeviceVelocityPtr vel, 5886747b715Smrg int dx, 5896747b715Smrg int dy, 5906747b715Smrg int time) 5916747b715Smrg{ 5926747b715Smrg float velocity; 5934642e01fSmrg 5946747b715Smrg vel->last_velocity = vel->velocity; 5956747b715Smrg 5966747b715Smrg FeedTrackers(vel, dx, dy, time); 5974642e01fSmrg 5986747b715Smrg velocity = QueryTrackers(vel, time); 5996747b715Smrg 6006747b715Smrg vel->velocity = velocity; 6016747b715Smrg return velocity == 0; 6026747b715Smrg} 6034642e01fSmrg 6044642e01fSmrg/** 6054642e01fSmrg * this flattens significant ( > 1) mickeys a little bit for more steady 6064642e01fSmrg * constant-velocity response 6074642e01fSmrg */ 6084642e01fSmrgstatic inline float 6094642e01fSmrgApplySimpleSoftening(int od, int d) 6104642e01fSmrg{ 6114642e01fSmrg float res = d; 6124642e01fSmrg if (d <= 1 && d >= -1) 6134642e01fSmrg return res; 6144642e01fSmrg if (d > od) 6154642e01fSmrg res -= 0.5; 6164642e01fSmrg else if (d < od) 6174642e01fSmrg res += 0.5; 6184642e01fSmrg return res; 6194642e01fSmrg} 6204642e01fSmrg 6214642e01fSmrg 6224642e01fSmrgstatic void 6234642e01fSmrgApplySofteningAndConstantDeceleration( 6246747b715Smrg DeviceVelocityPtr vel, 6254642e01fSmrg int dx, 6264642e01fSmrg int dy, 6274642e01fSmrg float* fdx, 6284642e01fSmrg float* fdy, 6294642e01fSmrg short do_soften) 6304642e01fSmrg{ 6316747b715Smrg if (do_soften && vel->use_softening) { 6326747b715Smrg *fdx = ApplySimpleSoftening(vel->last_dx, dx); 6336747b715Smrg *fdy = ApplySimpleSoftening(vel->last_dy, dy); 6344642e01fSmrg } else { 6354642e01fSmrg *fdx = dx; 6364642e01fSmrg *fdy = dy; 6374642e01fSmrg } 6384642e01fSmrg 6396747b715Smrg *fdx *= vel->const_acceleration; 6406747b715Smrg *fdy *= vel->const_acceleration; 6414642e01fSmrg} 6424642e01fSmrg 6434642e01fSmrg/* 6444642e01fSmrg * compute the acceleration for given velocity and enforce min_acceleartion 6454642e01fSmrg */ 6466747b715Smrgfloat 6474642e01fSmrgBasicComputeAcceleration( 6486747b715Smrg DeviceIntPtr dev, 6496747b715Smrg DeviceVelocityPtr vel, 6504642e01fSmrg float velocity, 6514642e01fSmrg float threshold, 6524642e01fSmrg float acc){ 6534642e01fSmrg 6544642e01fSmrg float result; 6556747b715Smrg result = vel->Profile(dev, vel, velocity, threshold, acc); 6564642e01fSmrg 6574642e01fSmrg /* enforce min_acceleration */ 6586747b715Smrg if (result < vel->min_acceleration) 6596747b715Smrg result = vel->min_acceleration; 6604642e01fSmrg return result; 6614642e01fSmrg} 6624642e01fSmrg 6634642e01fSmrg/** 6644642e01fSmrg * Compute acceleration. Takes into account averaging, nv-reset, etc. 6654642e01fSmrg */ 6664642e01fSmrgstatic float 6674642e01fSmrgComputeAcceleration( 6686747b715Smrg DeviceIntPtr dev, 6694642e01fSmrg DeviceVelocityPtr vel, 6704642e01fSmrg float threshold, 6714642e01fSmrg float acc){ 6724642e01fSmrg float res; 6734642e01fSmrg 6746747b715Smrg if(vel->velocity <= 0){ 6754642e01fSmrg DebugAccelF("(dix ptracc) profile skipped\n"); 6764642e01fSmrg /* 6776747b715Smrg * If we have no idea about device velocity, don't pretend it. 6784642e01fSmrg */ 6794642e01fSmrg return 1; 6804642e01fSmrg } 6814642e01fSmrg 6824642e01fSmrg if(vel->average_accel && vel->velocity != vel->last_velocity){ 6834642e01fSmrg /* use simpson's rule to average acceleration between 6844642e01fSmrg * current and previous velocity. 6854642e01fSmrg * Though being the more natural choice, it causes a minor delay 6864642e01fSmrg * in comparison, so it can be disabled. */ 6876747b715Smrg res = BasicComputeAcceleration( 6886747b715Smrg dev, vel, vel->velocity, threshold, acc); 6896747b715Smrg res += BasicComputeAcceleration( 6906747b715Smrg dev, vel, vel->last_velocity, threshold, acc); 6916747b715Smrg res += 4.0f * BasicComputeAcceleration(dev, vel, 6924642e01fSmrg (vel->last_velocity + vel->velocity) / 2, 6934642e01fSmrg threshold, acc); 6944642e01fSmrg res /= 6.0f; 6954642e01fSmrg DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n", 6964642e01fSmrg vel->velocity, vel->last_velocity, res); 6974642e01fSmrg return res; 6984642e01fSmrg }else{ 6996747b715Smrg res = BasicComputeAcceleration(dev, vel, 7006747b715Smrg vel->velocity, threshold, acc); 7014642e01fSmrg DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n", 7024642e01fSmrg vel->velocity, res); 7034642e01fSmrg return res; 7044642e01fSmrg } 7054642e01fSmrg} 7064642e01fSmrg 7074642e01fSmrg 7084642e01fSmrg/***************************************** 7094642e01fSmrg * Acceleration functions and profiles 7104642e01fSmrg ****************************************/ 7114642e01fSmrg 7124642e01fSmrg/** 7134642e01fSmrg * Polynomial function similar previous one, but with f(1) = 1 7144642e01fSmrg */ 7154642e01fSmrgstatic float 7164642e01fSmrgPolynomialAccelerationProfile( 7176747b715Smrg DeviceIntPtr dev, 7186747b715Smrg DeviceVelocityPtr vel, 7194642e01fSmrg float velocity, 7204642e01fSmrg float ignored, 7214642e01fSmrg float acc) 7224642e01fSmrg{ 7234642e01fSmrg return pow(velocity, (acc - 1.0) * 0.5); 7244642e01fSmrg} 7254642e01fSmrg 7264642e01fSmrg 7274642e01fSmrg/** 7284642e01fSmrg * returns acceleration for velocity. 7294642e01fSmrg * This profile selects the two functions like the old scheme did 7304642e01fSmrg */ 7314642e01fSmrgstatic float 7324642e01fSmrgClassicProfile( 7336747b715Smrg DeviceIntPtr dev, 7346747b715Smrg DeviceVelocityPtr vel, 7354642e01fSmrg float velocity, 7364642e01fSmrg float threshold, 7374642e01fSmrg float acc) 7384642e01fSmrg{ 7396747b715Smrg if (threshold > 0) { 7406747b715Smrg return SimpleSmoothProfile (dev, 7416747b715Smrg vel, 7424642e01fSmrg velocity, 7434642e01fSmrg threshold, 7444642e01fSmrg acc); 7454642e01fSmrg } else { 7466747b715Smrg return PolynomialAccelerationProfile (dev, 7476747b715Smrg vel, 7484642e01fSmrg velocity, 7494642e01fSmrg 0, 7504642e01fSmrg acc); 7514642e01fSmrg } 7524642e01fSmrg} 7534642e01fSmrg 7544642e01fSmrg 7554642e01fSmrg/** 7564642e01fSmrg * Power profile 7574642e01fSmrg * This has a completely smooth transition curve, i.e. no jumps in the 7584642e01fSmrg * derivatives. 7594642e01fSmrg * 7604642e01fSmrg * This has the expense of overall response dependency on min-acceleration. 7614642e01fSmrg * In effect, min_acceleration mimics const_acceleration in this profile. 7624642e01fSmrg */ 7634642e01fSmrgstatic float 7644642e01fSmrgPowerProfile( 7656747b715Smrg DeviceIntPtr dev, 7666747b715Smrg DeviceVelocityPtr vel, 7674642e01fSmrg float velocity, 7684642e01fSmrg float threshold, 7694642e01fSmrg float acc) 7704642e01fSmrg{ 7714642e01fSmrg float vel_dist; 7724642e01fSmrg 7734642e01fSmrg acc = (acc-1.0) * 0.1f + 1.0; /* without this, acc of 2 is unuseable */ 7744642e01fSmrg 7754642e01fSmrg if (velocity <= threshold) 7766747b715Smrg return vel->min_acceleration; 7774642e01fSmrg vel_dist = velocity - threshold; 7786747b715Smrg return (pow(acc, vel_dist)) * vel->min_acceleration; 7794642e01fSmrg} 7804642e01fSmrg 7814642e01fSmrg 7824642e01fSmrg/** 7834642e01fSmrg * just a smooth function in [0..1] -> [0..1] 7844642e01fSmrg * - point symmetry at 0.5 7854642e01fSmrg * - f'(0) = f'(1) = 0 7864642e01fSmrg * - starts faster than a sinoid 7874642e01fSmrg * - smoothness C1 (Cinf if you dare to ignore endpoints) 7884642e01fSmrg */ 7894642e01fSmrgstatic inline float 7904642e01fSmrgCalcPenumbralGradient(float x){ 7914642e01fSmrg x *= 2.0f; 7924642e01fSmrg x -= 1.0f; 7934642e01fSmrg return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI; 7944642e01fSmrg} 7954642e01fSmrg 7964642e01fSmrg 7974642e01fSmrg/** 7984642e01fSmrg * acceleration function similar to classic accelerated/unaccelerated, 7994642e01fSmrg * but with smooth transition in between (and towards zero for adaptive dec.). 8004642e01fSmrg */ 8014642e01fSmrgstatic float 8024642e01fSmrgSimpleSmoothProfile( 8036747b715Smrg DeviceIntPtr dev, 8046747b715Smrg DeviceVelocityPtr vel, 8054642e01fSmrg float velocity, 8064642e01fSmrg float threshold, 8074642e01fSmrg float acc) 8084642e01fSmrg{ 8094642e01fSmrg if(velocity < 1.0f) 8104642e01fSmrg return CalcPenumbralGradient(0.5 + velocity*0.5) * 2.0f - 1.0f; 8114642e01fSmrg if(threshold < 1.0f) 8124642e01fSmrg threshold = 1.0f; 8134642e01fSmrg if (velocity <= threshold) 8144642e01fSmrg return 1; 8154642e01fSmrg velocity /= threshold; 8164642e01fSmrg if (velocity >= acc) 8174642e01fSmrg return acc; 8184642e01fSmrg else 8194642e01fSmrg return 1.0f + (CalcPenumbralGradient(velocity/acc) * (acc - 1.0f)); 8204642e01fSmrg} 8214642e01fSmrg 8224642e01fSmrg 8234642e01fSmrg/** 8244642e01fSmrg * This profile uses the first half of the penumbral gradient as a start 8254642e01fSmrg * and then scales linearly. 8264642e01fSmrg */ 8274642e01fSmrgstatic float 8284642e01fSmrgSmoothLinearProfile( 8296747b715Smrg DeviceIntPtr dev, 8306747b715Smrg DeviceVelocityPtr vel, 8314642e01fSmrg float velocity, 8324642e01fSmrg float threshold, 8334642e01fSmrg float acc) 8344642e01fSmrg{ 8354642e01fSmrg float res, nv; 8364642e01fSmrg 8374642e01fSmrg if(acc > 1.0f) 8384642e01fSmrg acc -= 1.0f; /*this is so acc = 1 is no acceleration */ 8394642e01fSmrg else 8404642e01fSmrg return 1.0f; 8414642e01fSmrg 8424642e01fSmrg nv = (velocity - threshold) * acc * 0.5f; 8434642e01fSmrg 8444642e01fSmrg if(nv < 0){ 8454642e01fSmrg res = 0; 8464642e01fSmrg }else if(nv < 2){ 8474642e01fSmrg res = CalcPenumbralGradient(nv*0.25f)*2.0f; 8484642e01fSmrg }else{ 8494642e01fSmrg nv -= 2.0f; 8504642e01fSmrg res = nv * 2.0f / M_PI /* steepness of gradient at 0.5 */ 8514642e01fSmrg + 1.0f; /* gradient crosses 2|1 */ 8524642e01fSmrg } 8536747b715Smrg res += vel->min_acceleration; 8544642e01fSmrg return res; 8554642e01fSmrg} 8564642e01fSmrg 8574642e01fSmrg 8586747b715Smrg/** 8596747b715Smrg * From 0 to threshold, the response graduates smoothly from min_accel to 8606747b715Smrg * acceleration. Beyond threshold it is exactly the specified acceleration. 8616747b715Smrg */ 8626747b715Smrgstatic float 8636747b715SmrgSmoothLimitedProfile( 8646747b715Smrg DeviceIntPtr dev, 8656747b715Smrg DeviceVelocityPtr vel, 8666747b715Smrg float velocity, 8676747b715Smrg float threshold, 8686747b715Smrg float acc) 8696747b715Smrg{ 8706747b715Smrg float res; 8716747b715Smrg 8726747b715Smrg if(velocity >= threshold || threshold == 0.0f) 8736747b715Smrg return acc; 8746747b715Smrg 8756747b715Smrg velocity /= threshold; /* should be [0..1[ now */ 8766747b715Smrg 8776747b715Smrg res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration); 8786747b715Smrg 8796747b715Smrg return vel->min_acceleration + res; 8806747b715Smrg} 8816747b715Smrg 8826747b715Smrg 8834642e01fSmrgstatic float 8844642e01fSmrgLinearProfile( 8856747b715Smrg DeviceIntPtr dev, 8866747b715Smrg DeviceVelocityPtr vel, 8874642e01fSmrg float velocity, 8884642e01fSmrg float threshold, 8894642e01fSmrg float acc) 8904642e01fSmrg{ 8914642e01fSmrg return acc * velocity; 8924642e01fSmrg} 8934642e01fSmrg 8946747b715Smrgstatic float 8956747b715SmrgNoProfile( 8966747b715Smrg DeviceIntPtr dev, 8976747b715Smrg DeviceVelocityPtr vel, 8986747b715Smrg float velocity, 8996747b715Smrg float threshold, 9006747b715Smrg float acc) 9016747b715Smrg{ 9026747b715Smrg return 1.0f; 9036747b715Smrg} 9044642e01fSmrg 9056747b715Smrgstatic PointerAccelerationProfileFunc 9066747b715SmrgGetAccelerationProfile( 9076747b715Smrg DeviceVelocityPtr vel, 9084642e01fSmrg int profile_num) 9094642e01fSmrg{ 9104642e01fSmrg switch(profile_num){ 9114642e01fSmrg case AccelProfileClassic: 9126747b715Smrg return ClassicProfile; 9134642e01fSmrg case AccelProfileDeviceSpecific: 9146747b715Smrg return vel->deviceSpecificProfile; 9154642e01fSmrg case AccelProfilePolynomial: 9166747b715Smrg return PolynomialAccelerationProfile; 9174642e01fSmrg case AccelProfileSmoothLinear: 9186747b715Smrg return SmoothLinearProfile; 9194642e01fSmrg case AccelProfileSimple: 9206747b715Smrg return SimpleSmoothProfile; 9214642e01fSmrg case AccelProfilePower: 9226747b715Smrg return PowerProfile; 9234642e01fSmrg case AccelProfileLinear: 9246747b715Smrg return LinearProfile; 9256747b715Smrg case AccelProfileSmoothLimited: 9266747b715Smrg return SmoothLimitedProfile; 9276747b715Smrg case AccelProfileNone: 9286747b715Smrg return NoProfile; 9294642e01fSmrg default: 9306747b715Smrg return NULL; 9314642e01fSmrg } 9326747b715Smrg} 9336747b715Smrg 9346747b715Smrg/** 9356747b715Smrg * Set the profile by number. 9366747b715Smrg * Intended to make profiles exchangeable at runtime. 9376747b715Smrg * If you created a profile, give it a number here and in the header to 9386747b715Smrg * make it selectable. In case some profile-specific init is needed, here 9396747b715Smrg * would be a good place, since FreeVelocityData() also calls this with 9406747b715Smrg * PROFILE_UNINITIALIZE. 9416747b715Smrg * 9426747b715Smrg * returns FALSE if profile number is unavailable, TRUE otherwise. 9436747b715Smrg */ 9446747b715Smrgint 9456747b715SmrgSetAccelerationProfile( 9466747b715Smrg DeviceVelocityPtr vel, 9476747b715Smrg int profile_num) 9486747b715Smrg{ 9496747b715Smrg PointerAccelerationProfileFunc profile; 9506747b715Smrg profile = GetAccelerationProfile(vel, profile_num); 9516747b715Smrg 9526747b715Smrg if(profile == NULL && profile_num != PROFILE_UNINITIALIZE) 9536747b715Smrg return FALSE; 9546747b715Smrg 9559ace9065Smrg /* Here one could free old profile-private data */ 9569ace9065Smrg free(vel->profile_private); 9579ace9065Smrg vel->profile_private = NULL; 9584642e01fSmrg /* Here one could init profile-private data */ 9596747b715Smrg vel->Profile = profile; 9606747b715Smrg vel->statistics.profile_number = profile_num; 9614642e01fSmrg return TRUE; 9624642e01fSmrg} 9634642e01fSmrg 9644642e01fSmrg/********************************************** 9654642e01fSmrg * driver interaction 9664642e01fSmrg **********************************************/ 9674642e01fSmrg 9684642e01fSmrg 9694642e01fSmrg/** 9704642e01fSmrg * device-specific profile 9714642e01fSmrg * 9724642e01fSmrg * The device-specific profile is intended as a hook for a driver 9734642e01fSmrg * which may want to provide an own acceleration profile. 9744642e01fSmrg * It should not rely on profile-private data, instead 9754642e01fSmrg * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends). 9764642e01fSmrg * Users may override or choose it. 9774642e01fSmrg */ 9786747b715Smrgvoid 9794642e01fSmrgSetDeviceSpecificAccelerationProfile( 9806747b715Smrg DeviceVelocityPtr vel, 9814642e01fSmrg PointerAccelerationProfileFunc profile) 9824642e01fSmrg{ 9836747b715Smrg if(vel) 9846747b715Smrg vel->deviceSpecificProfile = profile; 9854642e01fSmrg} 9864642e01fSmrg 9874642e01fSmrg/** 9884642e01fSmrg * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if 9894642e01fSmrg * the predictable acceleration scheme is not in effect. 9904642e01fSmrg */ 9916747b715SmrgDeviceVelocityPtr 9924642e01fSmrgGetDevicePredictableAccelData( 9936747b715Smrg DeviceIntPtr dev) 9944642e01fSmrg{ 9954642e01fSmrg /*sanity check*/ 9966747b715Smrg if(!dev){ 9974642e01fSmrg ErrorF("[dix] accel: DeviceIntPtr was NULL"); 9984642e01fSmrg return NULL; 9994642e01fSmrg } 10006747b715Smrg if( dev->valuator && 10016747b715Smrg dev->valuator->accelScheme.AccelSchemeProc == 10024642e01fSmrg acceleratePointerPredictable && 10036747b715Smrg dev->valuator->accelScheme.accelData != NULL){ 10044642e01fSmrg 10056747b715Smrg return (DeviceVelocityPtr)dev->valuator->accelScheme.accelData; 10064642e01fSmrg } 10074642e01fSmrg return NULL; 10084642e01fSmrg} 10094642e01fSmrg 10104642e01fSmrg/******************************** 10114642e01fSmrg * acceleration schemes 10124642e01fSmrg *******************************/ 10134642e01fSmrg 10144642e01fSmrg/** 10154642e01fSmrg * Modifies valuators in-place. 10164642e01fSmrg * This version employs a velocity approximation algorithm to 10174642e01fSmrg * enable fine-grained predictable acceleration profiles. 10184642e01fSmrg */ 10194642e01fSmrgvoid 10204642e01fSmrgacceleratePointerPredictable( 10216747b715Smrg DeviceIntPtr dev, 10224642e01fSmrg int first_valuator, 10234642e01fSmrg int num_valuators, 10244642e01fSmrg int *valuators, 10254642e01fSmrg int evtime) 10264642e01fSmrg{ 10274642e01fSmrg float mult = 0.0; 10284642e01fSmrg int dx = 0, dy = 0; 10294642e01fSmrg int *px = NULL, *py = NULL; 10304642e01fSmrg DeviceVelocityPtr velocitydata = 10316747b715Smrg (DeviceVelocityPtr) dev->valuator->accelScheme.accelData; 10326747b715Smrg float fdx, fdy, tmp; /* no need to init */ 10336747b715Smrg Bool soften = TRUE; 10344642e01fSmrg 10354642e01fSmrg if (!num_valuators || !valuators || !velocitydata) 10364642e01fSmrg return; 10374642e01fSmrg 10386747b715Smrg if (velocitydata->statistics.profile_number == AccelProfileNone && 10396747b715Smrg velocitydata->const_acceleration == 1.0f) { 10406747b715Smrg return; /*we're inactive anyway, so skip the whole thing.*/ 10416747b715Smrg } 10426747b715Smrg 10434642e01fSmrg if (first_valuator == 0) { 10444642e01fSmrg dx = valuators[0]; 10454642e01fSmrg px = &valuators[0]; 10464642e01fSmrg } 10474642e01fSmrg if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { 10484642e01fSmrg dy = valuators[1 - first_valuator]; 10494642e01fSmrg py = &valuators[1 - first_valuator]; 10504642e01fSmrg } 10514642e01fSmrg 10524642e01fSmrg if (dx || dy){ 10536747b715Smrg /* reset non-visible state? */ 10546747b715Smrg if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) { 10556747b715Smrg soften = FALSE; 10564642e01fSmrg } 10574642e01fSmrg 10586747b715Smrg if (dev->ptrfeed && dev->ptrfeed->ctrl.num) { 10594642e01fSmrg /* invoke acceleration profile to determine acceleration */ 10606747b715Smrg mult = ComputeAcceleration (dev, velocitydata, 10616747b715Smrg dev->ptrfeed->ctrl.threshold, 10626747b715Smrg (float)dev->ptrfeed->ctrl.num / 10636747b715Smrg (float)dev->ptrfeed->ctrl.den); 10644642e01fSmrg 10654642e01fSmrg if(mult != 1.0 || velocitydata->const_acceleration != 1.0) { 10664642e01fSmrg ApplySofteningAndConstantDeceleration( velocitydata, 10676747b715Smrg dx, dy, 10686747b715Smrg &fdx, &fdy, 10696747b715Smrg (mult > 1.0) && soften); 10706747b715Smrg 10714642e01fSmrg if (dx) { 10726747b715Smrg tmp = mult * fdx + dev->last.remainder[0]; 10736747b715Smrg /* Since it may not be apparent: lrintf() does not offer 10746747b715Smrg * strong statements about rounding; however because we 10756747b715Smrg * process each axis conditionally, there's no danger 10766747b715Smrg * of a toggling remainder. Its lack of guarantees likely 10776747b715Smrg * makes it faster on the average target. */ 10786747b715Smrg *px = lrintf(tmp); 10796747b715Smrg dev->last.remainder[0] = tmp - (float)*px; 10804642e01fSmrg } 10814642e01fSmrg if (dy) { 10826747b715Smrg tmp = mult * fdy + dev->last.remainder[1]; 10836747b715Smrg *py = lrintf(tmp); 10846747b715Smrg dev->last.remainder[1] = tmp - (float)*py; 10854642e01fSmrg } 10866747b715Smrg DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n", 10876747b715Smrg *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy); 10884642e01fSmrg } 10894642e01fSmrg } 10904642e01fSmrg } 10914642e01fSmrg /* remember last motion delta (for softening/slow movement treatment) */ 10924642e01fSmrg velocitydata->last_dx = dx; 10934642e01fSmrg velocitydata->last_dy = dy; 10944642e01fSmrg} 10954642e01fSmrg 10964642e01fSmrg 10974642e01fSmrg 10984642e01fSmrg/** 10994642e01fSmrg * Originally a part of xf86PostMotionEvent; modifies valuators 11004642e01fSmrg * in-place. Retained mostly for embedded scenarios. 11014642e01fSmrg */ 11024642e01fSmrgvoid 11034642e01fSmrgacceleratePointerLightweight( 11046747b715Smrg DeviceIntPtr dev, 11054642e01fSmrg int first_valuator, 11064642e01fSmrg int num_valuators, 11074642e01fSmrg int *valuators, 11084642e01fSmrg int ignored) 11094642e01fSmrg{ 11104642e01fSmrg float mult = 0.0; 11114642e01fSmrg int dx = 0, dy = 0; 11124642e01fSmrg int *px = NULL, *py = NULL; 11134642e01fSmrg 11144642e01fSmrg if (!num_valuators || !valuators) 11154642e01fSmrg return; 11164642e01fSmrg 11174642e01fSmrg if (first_valuator == 0) { 11184642e01fSmrg dx = valuators[0]; 11194642e01fSmrg px = &valuators[0]; 11204642e01fSmrg } 11214642e01fSmrg if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { 11224642e01fSmrg dy = valuators[1 - first_valuator]; 11234642e01fSmrg py = &valuators[1 - first_valuator]; 11244642e01fSmrg } 11254642e01fSmrg 11264642e01fSmrg if (!dx && !dy) 11274642e01fSmrg return; 11284642e01fSmrg 11296747b715Smrg if (dev->ptrfeed && dev->ptrfeed->ctrl.num) { 11304642e01fSmrg /* modeled from xf86Events.c */ 11316747b715Smrg if (dev->ptrfeed->ctrl.threshold) { 11326747b715Smrg if ((abs(dx) + abs(dy)) >= dev->ptrfeed->ctrl.threshold) { 11336747b715Smrg dev->last.remainder[0] = ((float)dx * 11346747b715Smrg (float)(dev->ptrfeed->ctrl.num)) / 11356747b715Smrg (float)(dev->ptrfeed->ctrl.den) + 11366747b715Smrg dev->last.remainder[0]; 11374642e01fSmrg if (px) { 11386747b715Smrg *px = (int)dev->last.remainder[0]; 11396747b715Smrg dev->last.remainder[0] = dev->last.remainder[0] - 11404642e01fSmrg (float)(*px); 11414642e01fSmrg } 11424642e01fSmrg 11436747b715Smrg dev->last.remainder[1] = ((float)dy * 11446747b715Smrg (float)(dev->ptrfeed->ctrl.num)) / 11456747b715Smrg (float)(dev->ptrfeed->ctrl.den) + 11466747b715Smrg dev->last.remainder[1]; 11474642e01fSmrg if (py) { 11486747b715Smrg *py = (int)dev->last.remainder[1]; 11496747b715Smrg dev->last.remainder[1] = dev->last.remainder[1] - 11504642e01fSmrg (float)(*py); 11514642e01fSmrg } 11524642e01fSmrg } 11534642e01fSmrg } 11544642e01fSmrg else { 11554642e01fSmrg mult = pow((float)dx * (float)dx + (float)dy * (float)dy, 11566747b715Smrg ((float)(dev->ptrfeed->ctrl.num) / 11576747b715Smrg (float)(dev->ptrfeed->ctrl.den) - 1.0) / 11584642e01fSmrg 2.0) / 2.0; 11594642e01fSmrg if (dx) { 11606747b715Smrg dev->last.remainder[0] = mult * (float)dx + 11616747b715Smrg dev->last.remainder[0]; 11626747b715Smrg *px = (int)dev->last.remainder[0]; 11636747b715Smrg dev->last.remainder[0] = dev->last.remainder[0] - 11644642e01fSmrg (float)(*px); 11654642e01fSmrg } 11664642e01fSmrg if (dy) { 11676747b715Smrg dev->last.remainder[1] = mult * (float)dy + 11686747b715Smrg dev->last.remainder[1]; 11696747b715Smrg *py = (int)dev->last.remainder[1]; 11706747b715Smrg dev->last.remainder[1] = dev->last.remainder[1] - 11714642e01fSmrg (float)(*py); 11724642e01fSmrg } 11734642e01fSmrg } 11744642e01fSmrg } 11754642e01fSmrg} 1176