1706f2543Smrg/* 2706f2543Smrg * 3706f2543Smrg * Copyright © 2006-2009 Simon Thum simon dot thum at gmx dot de 4706f2543Smrg * 5706f2543Smrg * Permission is hereby granted, free of charge, to any person obtaining a 6706f2543Smrg * copy of this software and associated documentation files (the "Software"), 7706f2543Smrg * to deal in the Software without restriction, including without limitation 8706f2543Smrg * the rights to use, copy, modify, merge, publish, distribute, sublicense, 9706f2543Smrg * and/or sell copies of the Software, and to permit persons to whom the 10706f2543Smrg * Software is furnished to do so, subject to the following conditions: 11706f2543Smrg * 12706f2543Smrg * The above copyright notice and this permission notice (including the next 13706f2543Smrg * paragraph) shall be included in all copies or substantial portions of the 14706f2543Smrg * Software. 15706f2543Smrg * 16706f2543Smrg * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17706f2543Smrg * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18706f2543Smrg * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 19706f2543Smrg * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20706f2543Smrg * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 21706f2543Smrg * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 22706f2543Smrg * DEALINGS IN THE SOFTWARE. 23706f2543Smrg */ 24706f2543Smrg 25706f2543Smrg#ifdef HAVE_DIX_CONFIG_H 26706f2543Smrg#include <dix-config.h> 27706f2543Smrg#endif 28706f2543Smrg 29706f2543Smrg#include <math.h> 30706f2543Smrg#include <ptrveloc.h> 31706f2543Smrg#include <exevents.h> 32706f2543Smrg#include <X11/Xatom.h> 33706f2543Smrg 34706f2543Smrg#include <xserver-properties.h> 35706f2543Smrg 36706f2543Smrg/***************************************************************************** 37706f2543Smrg * Predictable pointer acceleration 38706f2543Smrg * 39706f2543Smrg * 2006-2009 by Simon Thum (simon [dot] thum [at] gmx de) 40706f2543Smrg * 41706f2543Smrg * Serves 3 complementary functions: 42706f2543Smrg * 1) provide a sophisticated ballistic velocity estimate to improve 43706f2543Smrg * the relation between velocity (of the device) and acceleration 44706f2543Smrg * 2) make arbitrary acceleration profiles possible 45706f2543Smrg * 3) decelerate by two means (constant and adaptive) if enabled 46706f2543Smrg * 47706f2543Smrg * Important concepts are the 48706f2543Smrg * 49706f2543Smrg * - Scheme 50706f2543Smrg * which selects the basic algorithm 51706f2543Smrg * (see devices.c/InitPointerAccelerationScheme) 52706f2543Smrg * - Profile 53706f2543Smrg * which returns an acceleration 54706f2543Smrg * for a given velocity 55706f2543Smrg * 56706f2543Smrg * The profile can be selected by the user at runtime. 57706f2543Smrg * The classic profile is intended to cleanly perform old-style 58706f2543Smrg * function selection (threshold =/!= 0) 59706f2543Smrg * 60706f2543Smrg ****************************************************************************/ 61706f2543Smrg 62706f2543Smrg/* fwds */ 63706f2543Smrgint 64706f2543SmrgSetAccelerationProfile(DeviceVelocityPtr vel, int profile_num); 65706f2543Smrgstatic float 66706f2543SmrgSimpleSmoothProfile(DeviceIntPtr dev, DeviceVelocityPtr vel, float velocity, 67706f2543Smrg float threshold, float acc); 68706f2543Smrgstatic PointerAccelerationProfileFunc 69706f2543SmrgGetAccelerationProfile(DeviceVelocityPtr vel, int profile_num); 70706f2543Smrg 71706f2543Smrg/*#define PTRACCEL_DEBUGGING*/ 72706f2543Smrg 73706f2543Smrg#ifdef PTRACCEL_DEBUGGING 74706f2543Smrg#define DebugAccelF ErrorF 75706f2543Smrg#else 76706f2543Smrg#define DebugAccelF(...) /* */ 77706f2543Smrg#endif 78706f2543Smrg 79706f2543Smrg/******************************** 80706f2543Smrg * Init/Uninit 81706f2543Smrg *******************************/ 82706f2543Smrg 83706f2543Smrg/* some int which is not a profile number */ 84706f2543Smrg#define PROFILE_UNINITIALIZE (-100) 85706f2543Smrg 86706f2543Smrg 87706f2543Smrg/** 88706f2543Smrg * Init struct so it should match the average case 89706f2543Smrg */ 90706f2543Smrgvoid 91706f2543SmrgInitVelocityData(DeviceVelocityPtr vel) 92706f2543Smrg{ 93706f2543Smrg memset(vel, 0, sizeof(DeviceVelocityRec)); 94706f2543Smrg 95706f2543Smrg vel->corr_mul = 10.0; /* dots per 10 milisecond should be usable */ 96706f2543Smrg vel->const_acceleration = 1.0; /* no acceleration/deceleration */ 97706f2543Smrg vel->reset_time = 300; 98706f2543Smrg vel->use_softening = 1; 99706f2543Smrg vel->min_acceleration = 1.0; /* don't decelerate */ 100706f2543Smrg vel->max_rel_diff = 0.2; 101706f2543Smrg vel->max_diff = 1.0; 102706f2543Smrg vel->initial_range = 2; 103706f2543Smrg vel->average_accel = TRUE; 104706f2543Smrg SetAccelerationProfile(vel, AccelProfileClassic); 105706f2543Smrg InitTrackers(vel, 16); 106706f2543Smrg} 107706f2543Smrg 108706f2543Smrg 109706f2543Smrg/** 110706f2543Smrg * Clean up 111706f2543Smrg */ 112706f2543Smrgvoid 113706f2543SmrgFreeVelocityData(DeviceVelocityPtr vel){ 114706f2543Smrg free(vel->tracker); 115706f2543Smrg SetAccelerationProfile(vel, PROFILE_UNINITIALIZE); 116706f2543Smrg} 117706f2543Smrg 118706f2543Smrg 119706f2543Smrg/* 120706f2543Smrg * dix uninit helper, called through scheme 121706f2543Smrg */ 122706f2543Smrgvoid 123706f2543SmrgAccelerationDefaultCleanup(DeviceIntPtr dev) 124706f2543Smrg{ 125706f2543Smrg /*sanity check*/ 126706f2543Smrg if( dev->valuator->accelScheme.AccelSchemeProc == acceleratePointerPredictable 127706f2543Smrg && dev->valuator->accelScheme.accelData != NULL){ 128706f2543Smrg dev->valuator->accelScheme.AccelSchemeProc = NULL; 129706f2543Smrg FreeVelocityData(dev->valuator->accelScheme.accelData); 130706f2543Smrg free(dev->valuator->accelScheme.accelData); 131706f2543Smrg dev->valuator->accelScheme.accelData = NULL; 132706f2543Smrg DeletePredictableAccelerationProperties(dev); 133706f2543Smrg } 134706f2543Smrg} 135706f2543Smrg 136706f2543Smrg 137706f2543Smrg/************************* 138706f2543Smrg * Input property support 139706f2543Smrg ************************/ 140706f2543Smrg 141706f2543Smrg/** 142706f2543Smrg * choose profile 143706f2543Smrg */ 144706f2543Smrgstatic int 145706f2543SmrgAccelSetProfileProperty(DeviceIntPtr dev, Atom atom, 146706f2543Smrg XIPropertyValuePtr val, BOOL checkOnly) 147706f2543Smrg{ 148706f2543Smrg DeviceVelocityPtr vel; 149706f2543Smrg int profile, *ptr = &profile; 150706f2543Smrg int rc; 151706f2543Smrg int nelem = 1; 152706f2543Smrg 153706f2543Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER)) 154706f2543Smrg return Success; 155706f2543Smrg 156706f2543Smrg vel = GetDevicePredictableAccelData(dev); 157706f2543Smrg if (!vel) 158706f2543Smrg return BadValue; 159706f2543Smrg rc = XIPropToInt(val, &nelem, &ptr); 160706f2543Smrg 161706f2543Smrg if(checkOnly) 162706f2543Smrg { 163706f2543Smrg if (rc) 164706f2543Smrg return rc; 165706f2543Smrg 166706f2543Smrg if (GetAccelerationProfile(vel, profile) == NULL) 167706f2543Smrg return BadValue; 168706f2543Smrg } else 169706f2543Smrg SetAccelerationProfile(vel, profile); 170706f2543Smrg 171706f2543Smrg return Success; 172706f2543Smrg} 173706f2543Smrg 174706f2543Smrgstatic long 175706f2543SmrgAccelInitProfileProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 176706f2543Smrg{ 177706f2543Smrg int profile = vel->statistics.profile_number; 178706f2543Smrg Atom prop_profile_number = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); 179706f2543Smrg 180706f2543Smrg XIChangeDeviceProperty(dev, prop_profile_number, XA_INTEGER, 32, 181706f2543Smrg PropModeReplace, 1, &profile, FALSE); 182706f2543Smrg XISetDevicePropertyDeletable(dev, prop_profile_number, FALSE); 183706f2543Smrg return XIRegisterPropertyHandler(dev, AccelSetProfileProperty, NULL, NULL); 184706f2543Smrg} 185706f2543Smrg 186706f2543Smrg/** 187706f2543Smrg * constant deceleration 188706f2543Smrg */ 189706f2543Smrgstatic int 190706f2543SmrgAccelSetDecelProperty(DeviceIntPtr dev, Atom atom, 191706f2543Smrg XIPropertyValuePtr val, BOOL checkOnly) 192706f2543Smrg{ 193706f2543Smrg DeviceVelocityPtr vel; 194706f2543Smrg float v, *ptr = &v; 195706f2543Smrg int rc; 196706f2543Smrg int nelem = 1; 197706f2543Smrg 198706f2543Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION)) 199706f2543Smrg return Success; 200706f2543Smrg 201706f2543Smrg vel = GetDevicePredictableAccelData(dev); 202706f2543Smrg if (!vel) 203706f2543Smrg return BadValue; 204706f2543Smrg rc = XIPropToFloat(val, &nelem, &ptr); 205706f2543Smrg 206706f2543Smrg if(checkOnly) 207706f2543Smrg { 208706f2543Smrg if (rc) 209706f2543Smrg return rc; 210706f2543Smrg return (v >= 1.0f) ? Success : BadValue; 211706f2543Smrg } 212706f2543Smrg 213706f2543Smrg if(v >= 1.0f) 214706f2543Smrg vel->const_acceleration = 1/v; 215706f2543Smrg 216706f2543Smrg return Success; 217706f2543Smrg} 218706f2543Smrg 219706f2543Smrgstatic long 220706f2543SmrgAccelInitDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 221706f2543Smrg{ 222706f2543Smrg float fval = 1.0/vel->const_acceleration; 223706f2543Smrg Atom prop_const_decel = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION); 224706f2543Smrg XIChangeDeviceProperty(dev, prop_const_decel, 225706f2543Smrg XIGetKnownProperty(XATOM_FLOAT), 32, 226706f2543Smrg PropModeReplace, 1, &fval, FALSE); 227706f2543Smrg XISetDevicePropertyDeletable(dev, prop_const_decel, FALSE); 228706f2543Smrg return XIRegisterPropertyHandler(dev, AccelSetDecelProperty, NULL, NULL); 229706f2543Smrg} 230706f2543Smrg 231706f2543Smrg 232706f2543Smrg/** 233706f2543Smrg * adaptive deceleration 234706f2543Smrg */ 235706f2543Smrgstatic int 236706f2543SmrgAccelSetAdaptDecelProperty(DeviceIntPtr dev, Atom atom, 237706f2543Smrg XIPropertyValuePtr val, BOOL checkOnly) 238706f2543Smrg{ 239706f2543Smrg DeviceVelocityPtr veloc; 240706f2543Smrg float v, *ptr = &v; 241706f2543Smrg int rc; 242706f2543Smrg int nelem = 1; 243706f2543Smrg 244706f2543Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION)) 245706f2543Smrg return Success; 246706f2543Smrg 247706f2543Smrg veloc = GetDevicePredictableAccelData(dev); 248706f2543Smrg if (!veloc) 249706f2543Smrg return BadValue; 250706f2543Smrg rc = XIPropToFloat(val, &nelem, &ptr); 251706f2543Smrg 252706f2543Smrg if(checkOnly) 253706f2543Smrg { 254706f2543Smrg if (rc) 255706f2543Smrg return rc; 256706f2543Smrg return (v >= 1.0f) ? Success : BadValue; 257706f2543Smrg } 258706f2543Smrg 259706f2543Smrg if(v >= 1.0f) 260706f2543Smrg veloc->min_acceleration = 1/v; 261706f2543Smrg 262706f2543Smrg return Success; 263706f2543Smrg} 264706f2543Smrg 265706f2543Smrgstatic long 266706f2543SmrgAccelInitAdaptDecelProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 267706f2543Smrg{ 268706f2543Smrg float fval = 1.0/vel->min_acceleration; 269706f2543Smrg Atom prop_adapt_decel = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION); 270706f2543Smrg 271706f2543Smrg XIChangeDeviceProperty(dev, prop_adapt_decel, XIGetKnownProperty(XATOM_FLOAT), 32, 272706f2543Smrg PropModeReplace, 1, &fval, FALSE); 273706f2543Smrg XISetDevicePropertyDeletable(dev, prop_adapt_decel, FALSE); 274706f2543Smrg return XIRegisterPropertyHandler(dev, AccelSetAdaptDecelProperty, NULL, NULL); 275706f2543Smrg} 276706f2543Smrg 277706f2543Smrg 278706f2543Smrg/** 279706f2543Smrg * velocity scaling 280706f2543Smrg */ 281706f2543Smrgstatic int 282706f2543SmrgAccelSetScaleProperty(DeviceIntPtr dev, Atom atom, 283706f2543Smrg XIPropertyValuePtr val, BOOL checkOnly) 284706f2543Smrg{ 285706f2543Smrg DeviceVelocityPtr vel; 286706f2543Smrg float v, *ptr = &v; 287706f2543Smrg int rc; 288706f2543Smrg int nelem = 1; 289706f2543Smrg 290706f2543Smrg if (atom != XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING)) 291706f2543Smrg return Success; 292706f2543Smrg 293706f2543Smrg vel = GetDevicePredictableAccelData(dev); 294706f2543Smrg if (!vel) 295706f2543Smrg return BadValue; 296706f2543Smrg rc = XIPropToFloat(val, &nelem, &ptr); 297706f2543Smrg 298706f2543Smrg if (checkOnly) 299706f2543Smrg { 300706f2543Smrg if (rc) 301706f2543Smrg return rc; 302706f2543Smrg 303706f2543Smrg return (v > 0) ? Success : BadValue; 304706f2543Smrg } 305706f2543Smrg 306706f2543Smrg if(v > 0) 307706f2543Smrg vel->corr_mul = v; 308706f2543Smrg 309706f2543Smrg return Success; 310706f2543Smrg} 311706f2543Smrg 312706f2543Smrgstatic long 313706f2543SmrgAccelInitScaleProperty(DeviceIntPtr dev, DeviceVelocityPtr vel) 314706f2543Smrg{ 315706f2543Smrg float fval = vel->corr_mul; 316706f2543Smrg Atom prop_velo_scale = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING); 317706f2543Smrg 318706f2543Smrg XIChangeDeviceProperty(dev, prop_velo_scale, XIGetKnownProperty(XATOM_FLOAT), 32, 319706f2543Smrg PropModeReplace, 1, &fval, FALSE); 320706f2543Smrg XISetDevicePropertyDeletable(dev, prop_velo_scale, FALSE); 321706f2543Smrg return XIRegisterPropertyHandler(dev, AccelSetScaleProperty, NULL, NULL); 322706f2543Smrg} 323706f2543Smrg 324706f2543SmrgBOOL 325706f2543SmrgInitializePredictableAccelerationProperties(DeviceIntPtr dev) 326706f2543Smrg{ 327706f2543Smrg DeviceVelocityPtr vel = GetDevicePredictableAccelData(dev); 328706f2543Smrg 329706f2543Smrg if(!vel) 330706f2543Smrg return FALSE; 331706f2543Smrg 332706f2543Smrg vel->prop_handlers[0] = AccelInitProfileProperty(dev, vel); 333706f2543Smrg vel->prop_handlers[1] = AccelInitDecelProperty(dev, vel); 334706f2543Smrg vel->prop_handlers[2] = AccelInitAdaptDecelProperty(dev, vel); 335706f2543Smrg vel->prop_handlers[3] = AccelInitScaleProperty(dev, vel); 336706f2543Smrg 337706f2543Smrg return TRUE; 338706f2543Smrg} 339706f2543Smrg 340706f2543SmrgBOOL 341706f2543SmrgDeletePredictableAccelerationProperties(DeviceIntPtr dev) 342706f2543Smrg{ 343706f2543Smrg DeviceVelocityPtr vel; 344706f2543Smrg Atom prop; 345706f2543Smrg int i; 346706f2543Smrg 347706f2543Smrg prop = XIGetKnownProperty(ACCEL_PROP_VELOCITY_SCALING); 348706f2543Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 349706f2543Smrg prop = XIGetKnownProperty(ACCEL_PROP_ADAPTIVE_DECELERATION); 350706f2543Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 351706f2543Smrg prop = XIGetKnownProperty(ACCEL_PROP_CONSTANT_DECELERATION); 352706f2543Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 353706f2543Smrg prop = XIGetKnownProperty(ACCEL_PROP_PROFILE_NUMBER); 354706f2543Smrg XIDeleteDeviceProperty(dev, prop, FALSE); 355706f2543Smrg 356706f2543Smrg vel = GetDevicePredictableAccelData(dev); 357706f2543Smrg for (i = 0; vel && i < NPROPS_PREDICTABLE_ACCEL; i++) 358706f2543Smrg if (vel->prop_handlers[i]) 359706f2543Smrg XIUnregisterPropertyHandler(dev, vel->prop_handlers[i]); 360706f2543Smrg 361706f2543Smrg return TRUE; 362706f2543Smrg} 363706f2543Smrg 364706f2543Smrg/********************* 365706f2543Smrg * Tracking logic 366706f2543Smrg ********************/ 367706f2543Smrg 368706f2543Smrgvoid 369706f2543SmrgInitTrackers(DeviceVelocityPtr vel, int ntracker) 370706f2543Smrg{ 371706f2543Smrg if(ntracker < 1){ 372706f2543Smrg ErrorF("(dix ptracc) invalid number of trackers\n"); 373706f2543Smrg return; 374706f2543Smrg } 375706f2543Smrg free(vel->tracker); 376706f2543Smrg vel->tracker = (MotionTrackerPtr)malloc(ntracker * sizeof(MotionTracker)); 377706f2543Smrg memset(vel->tracker, 0, ntracker * sizeof(MotionTracker)); 378706f2543Smrg vel->num_tracker = ntracker; 379706f2543Smrg} 380706f2543Smrg 381706f2543Smrg/** 382706f2543Smrg * return a bit field of possible directions. 383706f2543Smrg * 0 = N, 2 = E, 4 = S, 6 = W, in-between is as you guess. 384706f2543Smrg * There's no reason against widening to more precise directions (<45 degrees), 385706f2543Smrg * should it not perform well. All this is needed for is sort out non-linear 386706f2543Smrg * motion, so precision isn't paramount. However, one should not flag direction 387706f2543Smrg * too narrow, since it would then cut the linear segment to zero size way too 388706f2543Smrg * often. 389706f2543Smrg */ 390706f2543Smrgstatic int 391706f2543SmrgDoGetDirection(int dx, int dy){ 392706f2543Smrg float r; 393706f2543Smrg int i1, i2; 394706f2543Smrg /* on insignificant mickeys, flag 135 degrees */ 395706f2543Smrg if(abs(dx) < 2 && abs(dy) < 2){ 396706f2543Smrg /* first check diagonal cases */ 397706f2543Smrg if(dx > 0 && dy > 0) 398706f2543Smrg return 4+8+16; 399706f2543Smrg if(dx > 0 && dy < 0) 400706f2543Smrg return 1+2+4; 401706f2543Smrg if(dx < 0 && dy < 0) 402706f2543Smrg return 1+128+64; 403706f2543Smrg if(dx < 0 && dy > 0) 404706f2543Smrg return 16+32+64; 405706f2543Smrg /* check axis-aligned directions */ 406706f2543Smrg if(dx > 0) 407706f2543Smrg return 2+4+8; /*E*/ 408706f2543Smrg if(dx < 0) 409706f2543Smrg return 128+64+32; /*W*/ 410706f2543Smrg if(dy > 0) 411706f2543Smrg return 32+16+8; /*S*/ 412706f2543Smrg if(dy < 0) 413706f2543Smrg return 128+1+2; /*N*/ 414706f2543Smrg return 255; /* shouldn't happen */ 415706f2543Smrg } 416706f2543Smrg /* else, compute angle and set appropriate flags */ 417706f2543Smrg#ifdef _ISOC99_SOURCE 418706f2543Smrg r = atan2f(dy, dx); 419706f2543Smrg#else 420706f2543Smrg r = atan2(dy, dx); 421706f2543Smrg#endif 422706f2543Smrg /* find direction. We avoid r to become negative, 423706f2543Smrg * since C has no well-defined modulo for such cases. */ 424706f2543Smrg r = (r+(M_PI*2.5))/(M_PI/4); 425706f2543Smrg /* this intends to flag 2 directions (90 degrees), 426706f2543Smrg * except on very well-aligned mickeys. */ 427706f2543Smrg i1 = (int)(r+0.1) % 8; 428706f2543Smrg i2 = (int)(r+0.9) % 8; 429706f2543Smrg if(i1 < 0 || i1 > 7 || i2 < 0 || i2 > 7) 430706f2543Smrg return 255; /* shouldn't happen */ 431706f2543Smrg return 1 << i1 | 1 << i2; 432706f2543Smrg} 433706f2543Smrg 434706f2543Smrg#define DIRECTION_CACHE_RANGE 5 435706f2543Smrg#define DIRECTION_CACHE_SIZE (DIRECTION_CACHE_RANGE*2+1) 436706f2543Smrg 437706f2543Smrg/* cache DoGetDirection(). */ 438706f2543Smrgstatic int 439706f2543SmrgGetDirection(int dx, int dy){ 440706f2543Smrg static int cache[DIRECTION_CACHE_SIZE][DIRECTION_CACHE_SIZE]; 441706f2543Smrg int i; 442706f2543Smrg if (abs(dx) <= DIRECTION_CACHE_RANGE && 443706f2543Smrg abs(dy) <= DIRECTION_CACHE_RANGE) { 444706f2543Smrg /* cacheable */ 445706f2543Smrg i = cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy]; 446706f2543Smrg if(i != 0){ 447706f2543Smrg return i; 448706f2543Smrg }else{ 449706f2543Smrg i = DoGetDirection(dx, dy); 450706f2543Smrg cache[DIRECTION_CACHE_RANGE+dx][DIRECTION_CACHE_RANGE+dy] = i; 451706f2543Smrg return i; 452706f2543Smrg } 453706f2543Smrg }else{ 454706f2543Smrg /* non-cacheable */ 455706f2543Smrg return DoGetDirection(dx, dy); 456706f2543Smrg } 457706f2543Smrg} 458706f2543Smrg 459706f2543Smrg#undef DIRECTION_CACHE_RANGE 460706f2543Smrg#undef DIRECTION_CACHE_SIZE 461706f2543Smrg 462706f2543Smrg 463706f2543Smrg/* convert offset (age) to array index */ 464706f2543Smrg#define TRACKER_INDEX(s, d) (((s)->num_tracker + (s)->cur_tracker - (d)) % (s)->num_tracker) 465706f2543Smrg 466706f2543Smrgstatic inline void 467706f2543SmrgFeedTrackers(DeviceVelocityPtr vel, int dx, int dy, int cur_t) 468706f2543Smrg{ 469706f2543Smrg int n; 470706f2543Smrg for(n = 0; n < vel->num_tracker; n++){ 471706f2543Smrg vel->tracker[n].dx += dx; 472706f2543Smrg vel->tracker[n].dy += dy; 473706f2543Smrg } 474706f2543Smrg n = (vel->cur_tracker + 1) % vel->num_tracker; 475706f2543Smrg vel->tracker[n].dx = 0; 476706f2543Smrg vel->tracker[n].dy = 0; 477706f2543Smrg vel->tracker[n].time = cur_t; 478706f2543Smrg vel->tracker[n].dir = GetDirection(dx, dy); 479706f2543Smrg DebugAccelF("(dix prtacc) motion [dx: %i dy: %i dir:%i diff: %i]\n", 480706f2543Smrg dx, dy, vel->tracker[n].dir, 481706f2543Smrg cur_t - vel->tracker[vel->cur_tracker].time); 482706f2543Smrg vel->cur_tracker = n; 483706f2543Smrg} 484706f2543Smrg 485706f2543Smrg/** 486706f2543Smrg * calc velocity for given tracker, with 487706f2543Smrg * velocity scaling. 488706f2543Smrg * This assumes linear motion. 489706f2543Smrg */ 490706f2543Smrgstatic float 491706f2543SmrgCalcTracker(DeviceVelocityPtr vel, int offset, int cur_t){ 492706f2543Smrg int index = TRACKER_INDEX(vel, offset); 493706f2543Smrg float dist = sqrt( vel->tracker[index].dx * vel->tracker[index].dx 494706f2543Smrg + vel->tracker[index].dy * vel->tracker[index].dy); 495706f2543Smrg int dtime = cur_t - vel->tracker[index].time; 496706f2543Smrg if(dtime > 0) 497706f2543Smrg return dist / dtime; 498706f2543Smrg else 499706f2543Smrg return 0;/* synonymous for NaN, since we're not C99 */ 500706f2543Smrg} 501706f2543Smrg 502706f2543Smrg/* find the most plausible velocity. That is, the most distant 503706f2543Smrg * (in time) tracker which isn't too old, beyond a linear partition, 504706f2543Smrg * or simply too much off initial velocity. 505706f2543Smrg * 506706f2543Smrg * May return 0. 507706f2543Smrg */ 508706f2543Smrgstatic float 509706f2543SmrgQueryTrackers(DeviceVelocityPtr vel, int cur_t){ 510706f2543Smrg int n, offset, dir = 255, i = -1, age_ms; 511706f2543Smrg /* initial velocity: a low-offset, valid velocity */ 512706f2543Smrg float iveloc = 0, res = 0, tmp, vdiff; 513706f2543Smrg float vfac = vel->corr_mul * vel->const_acceleration; /* premultiply */ 514706f2543Smrg /* loop from current to older data */ 515706f2543Smrg for(offset = 1; offset < vel->num_tracker; offset++){ 516706f2543Smrg n = TRACKER_INDEX(vel, offset); 517706f2543Smrg 518706f2543Smrg age_ms = cur_t - vel->tracker[n].time; 519706f2543Smrg 520706f2543Smrg /* bail out if data is too old and protect from overrun */ 521706f2543Smrg if (age_ms >= vel->reset_time || age_ms < 0) { 522706f2543Smrg DebugAccelF("(dix prtacc) query: tracker too old\n"); 523706f2543Smrg break; 524706f2543Smrg } 525706f2543Smrg 526706f2543Smrg /* 527706f2543Smrg * this heuristic avoids using the linear-motion velocity formula 528706f2543Smrg * in CalcTracker() on motion that isn't exactly linear. So to get 529706f2543Smrg * even more precision we could subdivide as a final step, so possible 530706f2543Smrg * non-linearities are accounted for. 531706f2543Smrg */ 532706f2543Smrg dir &= vel->tracker[n].dir; 533706f2543Smrg if(dir == 0){ 534706f2543Smrg DebugAccelF("(dix prtacc) query: no longer linear\n"); 535706f2543Smrg /* instead of breaking it we might also inspect the partition after, 536706f2543Smrg * but actual improvement with this is probably rare. */ 537706f2543Smrg break; 538706f2543Smrg } 539706f2543Smrg 540706f2543Smrg tmp = CalcTracker(vel, offset, cur_t) * vfac; 541706f2543Smrg 542706f2543Smrg if ((iveloc == 0 || offset <= vel->initial_range) && tmp != 0) { 543706f2543Smrg /* set initial velocity and result */ 544706f2543Smrg res = iveloc = tmp; 545706f2543Smrg i = offset; 546706f2543Smrg } else if (iveloc != 0 && tmp != 0) { 547706f2543Smrg vdiff = fabs(iveloc - tmp); 548706f2543Smrg if (vdiff <= vel->max_diff || 549706f2543Smrg vdiff/(iveloc + tmp) < vel->max_rel_diff) { 550706f2543Smrg /* we're in range with the initial velocity, 551706f2543Smrg * so this result is likely better 552706f2543Smrg * (it contains more information). */ 553706f2543Smrg res = tmp; 554706f2543Smrg i = offset; 555706f2543Smrg }else{ 556706f2543Smrg /* we're not in range, quit - it won't get better. */ 557706f2543Smrg DebugAccelF("(dix prtacc) query: tracker too different:" 558706f2543Smrg " old %2.2f initial %2.2f diff: %2.2f\n", 559706f2543Smrg tmp, iveloc, vdiff); 560706f2543Smrg break; 561706f2543Smrg } 562706f2543Smrg } 563706f2543Smrg } 564706f2543Smrg if(offset == vel->num_tracker){ 565706f2543Smrg DebugAccelF("(dix prtacc) query: last tracker in effect\n"); 566706f2543Smrg i = vel->num_tracker-1; 567706f2543Smrg } 568706f2543Smrg if(i>=0){ 569706f2543Smrg n = TRACKER_INDEX(vel, i); 570706f2543Smrg DebugAccelF("(dix prtacc) result: offset %i [dx: %i dy: %i diff: %i]\n", 571706f2543Smrg i, 572706f2543Smrg vel->tracker[n].dx, 573706f2543Smrg vel->tracker[n].dy, 574706f2543Smrg cur_t - vel->tracker[n].time); 575706f2543Smrg } 576706f2543Smrg return res; 577706f2543Smrg} 578706f2543Smrg 579706f2543Smrg#undef TRACKER_INDEX 580706f2543Smrg 581706f2543Smrg/** 582706f2543Smrg * Perform velocity approximation based on 2D 'mickeys' (mouse motion delta). 583706f2543Smrg * return true if non-visible state reset is suggested 584706f2543Smrg */ 585706f2543Smrgshort 586706f2543SmrgProcessVelocityData2D( 587706f2543Smrg DeviceVelocityPtr vel, 588706f2543Smrg int dx, 589706f2543Smrg int dy, 590706f2543Smrg int time) 591706f2543Smrg{ 592706f2543Smrg float velocity; 593706f2543Smrg 594706f2543Smrg vel->last_velocity = vel->velocity; 595706f2543Smrg 596706f2543Smrg FeedTrackers(vel, dx, dy, time); 597706f2543Smrg 598706f2543Smrg velocity = QueryTrackers(vel, time); 599706f2543Smrg 600706f2543Smrg vel->velocity = velocity; 601706f2543Smrg return velocity == 0; 602706f2543Smrg} 603706f2543Smrg 604706f2543Smrg/** 605706f2543Smrg * this flattens significant ( > 1) mickeys a little bit for more steady 606706f2543Smrg * constant-velocity response 607706f2543Smrg */ 608706f2543Smrgstatic inline float 609706f2543SmrgApplySimpleSoftening(int od, int d) 610706f2543Smrg{ 611706f2543Smrg float res = d; 612706f2543Smrg if (d <= 1 && d >= -1) 613706f2543Smrg return res; 614706f2543Smrg if (d > od) 615706f2543Smrg res -= 0.5; 616706f2543Smrg else if (d < od) 617706f2543Smrg res += 0.5; 618706f2543Smrg return res; 619706f2543Smrg} 620706f2543Smrg 621706f2543Smrg 622706f2543Smrgstatic void 623706f2543SmrgApplySofteningAndConstantDeceleration( 624706f2543Smrg DeviceVelocityPtr vel, 625706f2543Smrg int dx, 626706f2543Smrg int dy, 627706f2543Smrg float* fdx, 628706f2543Smrg float* fdy, 629706f2543Smrg short do_soften) 630706f2543Smrg{ 631706f2543Smrg if (do_soften && vel->use_softening) { 632706f2543Smrg *fdx = ApplySimpleSoftening(vel->last_dx, dx); 633706f2543Smrg *fdy = ApplySimpleSoftening(vel->last_dy, dy); 634706f2543Smrg } else { 635706f2543Smrg *fdx = dx; 636706f2543Smrg *fdy = dy; 637706f2543Smrg } 638706f2543Smrg 639706f2543Smrg *fdx *= vel->const_acceleration; 640706f2543Smrg *fdy *= vel->const_acceleration; 641706f2543Smrg} 642706f2543Smrg 643706f2543Smrg/* 644706f2543Smrg * compute the acceleration for given velocity and enforce min_acceleartion 645706f2543Smrg */ 646706f2543Smrgfloat 647706f2543SmrgBasicComputeAcceleration( 648706f2543Smrg DeviceIntPtr dev, 649706f2543Smrg DeviceVelocityPtr vel, 650706f2543Smrg float velocity, 651706f2543Smrg float threshold, 652706f2543Smrg float acc){ 653706f2543Smrg 654706f2543Smrg float result; 655706f2543Smrg result = vel->Profile(dev, vel, velocity, threshold, acc); 656706f2543Smrg 657706f2543Smrg /* enforce min_acceleration */ 658706f2543Smrg if (result < vel->min_acceleration) 659706f2543Smrg result = vel->min_acceleration; 660706f2543Smrg return result; 661706f2543Smrg} 662706f2543Smrg 663706f2543Smrg/** 664706f2543Smrg * Compute acceleration. Takes into account averaging, nv-reset, etc. 665706f2543Smrg */ 666706f2543Smrgstatic float 667706f2543SmrgComputeAcceleration( 668706f2543Smrg DeviceIntPtr dev, 669706f2543Smrg DeviceVelocityPtr vel, 670706f2543Smrg float threshold, 671706f2543Smrg float acc){ 672706f2543Smrg float res; 673706f2543Smrg 674706f2543Smrg if(vel->velocity <= 0){ 675706f2543Smrg DebugAccelF("(dix ptracc) profile skipped\n"); 676706f2543Smrg /* 677706f2543Smrg * If we have no idea about device velocity, don't pretend it. 678706f2543Smrg */ 679706f2543Smrg return 1; 680706f2543Smrg } 681706f2543Smrg 682706f2543Smrg if(vel->average_accel && vel->velocity != vel->last_velocity){ 683706f2543Smrg /* use simpson's rule to average acceleration between 684706f2543Smrg * current and previous velocity. 685706f2543Smrg * Though being the more natural choice, it causes a minor delay 686706f2543Smrg * in comparison, so it can be disabled. */ 687706f2543Smrg res = BasicComputeAcceleration( 688706f2543Smrg dev, vel, vel->velocity, threshold, acc); 689706f2543Smrg res += BasicComputeAcceleration( 690706f2543Smrg dev, vel, vel->last_velocity, threshold, acc); 691706f2543Smrg res += 4.0f * BasicComputeAcceleration(dev, vel, 692706f2543Smrg (vel->last_velocity + vel->velocity) / 2, 693706f2543Smrg threshold, acc); 694706f2543Smrg res /= 6.0f; 695706f2543Smrg DebugAccelF("(dix ptracc) profile average [%.2f ... %.2f] is %.3f\n", 696706f2543Smrg vel->velocity, vel->last_velocity, res); 697706f2543Smrg return res; 698706f2543Smrg }else{ 699706f2543Smrg res = BasicComputeAcceleration(dev, vel, 700706f2543Smrg vel->velocity, threshold, acc); 701706f2543Smrg DebugAccelF("(dix ptracc) profile sample [%.2f] is %.3f\n", 702706f2543Smrg vel->velocity, res); 703706f2543Smrg return res; 704706f2543Smrg } 705706f2543Smrg} 706706f2543Smrg 707706f2543Smrg 708706f2543Smrg/***************************************** 709706f2543Smrg * Acceleration functions and profiles 710706f2543Smrg ****************************************/ 711706f2543Smrg 712706f2543Smrg/** 713706f2543Smrg * Polynomial function similar previous one, but with f(1) = 1 714706f2543Smrg */ 715706f2543Smrgstatic float 716706f2543SmrgPolynomialAccelerationProfile( 717706f2543Smrg DeviceIntPtr dev, 718706f2543Smrg DeviceVelocityPtr vel, 719706f2543Smrg float velocity, 720706f2543Smrg float ignored, 721706f2543Smrg float acc) 722706f2543Smrg{ 723706f2543Smrg return pow(velocity, (acc - 1.0) * 0.5); 724706f2543Smrg} 725706f2543Smrg 726706f2543Smrg 727706f2543Smrg/** 728706f2543Smrg * returns acceleration for velocity. 729706f2543Smrg * This profile selects the two functions like the old scheme did 730706f2543Smrg */ 731706f2543Smrgstatic float 732706f2543SmrgClassicProfile( 733706f2543Smrg DeviceIntPtr dev, 734706f2543Smrg DeviceVelocityPtr vel, 735706f2543Smrg float velocity, 736706f2543Smrg float threshold, 737706f2543Smrg float acc) 738706f2543Smrg{ 739706f2543Smrg if (threshold > 0) { 740706f2543Smrg return SimpleSmoothProfile (dev, 741706f2543Smrg vel, 742706f2543Smrg velocity, 743706f2543Smrg threshold, 744706f2543Smrg acc); 745706f2543Smrg } else { 746706f2543Smrg return PolynomialAccelerationProfile (dev, 747706f2543Smrg vel, 748706f2543Smrg velocity, 749706f2543Smrg 0, 750706f2543Smrg acc); 751706f2543Smrg } 752706f2543Smrg} 753706f2543Smrg 754706f2543Smrg 755706f2543Smrg/** 756706f2543Smrg * Power profile 757706f2543Smrg * This has a completely smooth transition curve, i.e. no jumps in the 758706f2543Smrg * derivatives. 759706f2543Smrg * 760706f2543Smrg * This has the expense of overall response dependency on min-acceleration. 761706f2543Smrg * In effect, min_acceleration mimics const_acceleration in this profile. 762706f2543Smrg */ 763706f2543Smrgstatic float 764706f2543SmrgPowerProfile( 765706f2543Smrg DeviceIntPtr dev, 766706f2543Smrg DeviceVelocityPtr vel, 767706f2543Smrg float velocity, 768706f2543Smrg float threshold, 769706f2543Smrg float acc) 770706f2543Smrg{ 771706f2543Smrg float vel_dist; 772706f2543Smrg 773706f2543Smrg acc = (acc-1.0) * 0.1f + 1.0; /* without this, acc of 2 is unuseable */ 774706f2543Smrg 775706f2543Smrg if (velocity <= threshold) 776706f2543Smrg return vel->min_acceleration; 777706f2543Smrg vel_dist = velocity - threshold; 778706f2543Smrg return (pow(acc, vel_dist)) * vel->min_acceleration; 779706f2543Smrg} 780706f2543Smrg 781706f2543Smrg 782706f2543Smrg/** 783706f2543Smrg * just a smooth function in [0..1] -> [0..1] 784706f2543Smrg * - point symmetry at 0.5 785706f2543Smrg * - f'(0) = f'(1) = 0 786706f2543Smrg * - starts faster than a sinoid 787706f2543Smrg * - smoothness C1 (Cinf if you dare to ignore endpoints) 788706f2543Smrg */ 789706f2543Smrgstatic inline float 790706f2543SmrgCalcPenumbralGradient(float x){ 791706f2543Smrg x *= 2.0f; 792706f2543Smrg x -= 1.0f; 793706f2543Smrg return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI; 794706f2543Smrg} 795706f2543Smrg 796706f2543Smrg 797706f2543Smrg/** 798706f2543Smrg * acceleration function similar to classic accelerated/unaccelerated, 799706f2543Smrg * but with smooth transition in between (and towards zero for adaptive dec.). 800706f2543Smrg */ 801706f2543Smrgstatic float 802706f2543SmrgSimpleSmoothProfile( 803706f2543Smrg DeviceIntPtr dev, 804706f2543Smrg DeviceVelocityPtr vel, 805706f2543Smrg float velocity, 806706f2543Smrg float threshold, 807706f2543Smrg float acc) 808706f2543Smrg{ 809706f2543Smrg if(velocity < 1.0f) 810706f2543Smrg return CalcPenumbralGradient(0.5 + velocity*0.5) * 2.0f - 1.0f; 811706f2543Smrg if(threshold < 1.0f) 812706f2543Smrg threshold = 1.0f; 813706f2543Smrg if (velocity <= threshold) 814706f2543Smrg return 1; 815706f2543Smrg velocity /= threshold; 816706f2543Smrg if (velocity >= acc) 817706f2543Smrg return acc; 818706f2543Smrg else 819706f2543Smrg return 1.0f + (CalcPenumbralGradient(velocity/acc) * (acc - 1.0f)); 820706f2543Smrg} 821706f2543Smrg 822706f2543Smrg 823706f2543Smrg/** 824706f2543Smrg * This profile uses the first half of the penumbral gradient as a start 825706f2543Smrg * and then scales linearly. 826706f2543Smrg */ 827706f2543Smrgstatic float 828706f2543SmrgSmoothLinearProfile( 829706f2543Smrg DeviceIntPtr dev, 830706f2543Smrg DeviceVelocityPtr vel, 831706f2543Smrg float velocity, 832706f2543Smrg float threshold, 833706f2543Smrg float acc) 834706f2543Smrg{ 835706f2543Smrg float res, nv; 836706f2543Smrg 837706f2543Smrg if(acc > 1.0f) 838706f2543Smrg acc -= 1.0f; /*this is so acc = 1 is no acceleration */ 839706f2543Smrg else 840706f2543Smrg return 1.0f; 841706f2543Smrg 842706f2543Smrg nv = (velocity - threshold) * acc * 0.5f; 843706f2543Smrg 844706f2543Smrg if(nv < 0){ 845706f2543Smrg res = 0; 846706f2543Smrg }else if(nv < 2){ 847706f2543Smrg res = CalcPenumbralGradient(nv*0.25f)*2.0f; 848706f2543Smrg }else{ 849706f2543Smrg nv -= 2.0f; 850706f2543Smrg res = nv * 2.0f / M_PI /* steepness of gradient at 0.5 */ 851706f2543Smrg + 1.0f; /* gradient crosses 2|1 */ 852706f2543Smrg } 853706f2543Smrg res += vel->min_acceleration; 854706f2543Smrg return res; 855706f2543Smrg} 856706f2543Smrg 857706f2543Smrg 858706f2543Smrg/** 859706f2543Smrg * From 0 to threshold, the response graduates smoothly from min_accel to 860706f2543Smrg * acceleration. Beyond threshold it is exactly the specified acceleration. 861706f2543Smrg */ 862706f2543Smrgstatic float 863706f2543SmrgSmoothLimitedProfile( 864706f2543Smrg DeviceIntPtr dev, 865706f2543Smrg DeviceVelocityPtr vel, 866706f2543Smrg float velocity, 867706f2543Smrg float threshold, 868706f2543Smrg float acc) 869706f2543Smrg{ 870706f2543Smrg float res; 871706f2543Smrg 872706f2543Smrg if(velocity >= threshold || threshold == 0.0f) 873706f2543Smrg return acc; 874706f2543Smrg 875706f2543Smrg velocity /= threshold; /* should be [0..1[ now */ 876706f2543Smrg 877706f2543Smrg res = CalcPenumbralGradient(velocity) * (acc - vel->min_acceleration); 878706f2543Smrg 879706f2543Smrg return vel->min_acceleration + res; 880706f2543Smrg} 881706f2543Smrg 882706f2543Smrg 883706f2543Smrgstatic float 884706f2543SmrgLinearProfile( 885706f2543Smrg DeviceIntPtr dev, 886706f2543Smrg DeviceVelocityPtr vel, 887706f2543Smrg float velocity, 888706f2543Smrg float threshold, 889706f2543Smrg float acc) 890706f2543Smrg{ 891706f2543Smrg return acc * velocity; 892706f2543Smrg} 893706f2543Smrg 894706f2543Smrgstatic float 895706f2543SmrgNoProfile( 896706f2543Smrg DeviceIntPtr dev, 897706f2543Smrg DeviceVelocityPtr vel, 898706f2543Smrg float velocity, 899706f2543Smrg float threshold, 900706f2543Smrg float acc) 901706f2543Smrg{ 902706f2543Smrg return 1.0f; 903706f2543Smrg} 904706f2543Smrg 905706f2543Smrgstatic PointerAccelerationProfileFunc 906706f2543SmrgGetAccelerationProfile( 907706f2543Smrg DeviceVelocityPtr vel, 908706f2543Smrg int profile_num) 909706f2543Smrg{ 910706f2543Smrg switch(profile_num){ 911706f2543Smrg case AccelProfileClassic: 912706f2543Smrg return ClassicProfile; 913706f2543Smrg case AccelProfileDeviceSpecific: 914706f2543Smrg return vel->deviceSpecificProfile; 915706f2543Smrg case AccelProfilePolynomial: 916706f2543Smrg return PolynomialAccelerationProfile; 917706f2543Smrg case AccelProfileSmoothLinear: 918706f2543Smrg return SmoothLinearProfile; 919706f2543Smrg case AccelProfileSimple: 920706f2543Smrg return SimpleSmoothProfile; 921706f2543Smrg case AccelProfilePower: 922706f2543Smrg return PowerProfile; 923706f2543Smrg case AccelProfileLinear: 924706f2543Smrg return LinearProfile; 925706f2543Smrg case AccelProfileSmoothLimited: 926706f2543Smrg return SmoothLimitedProfile; 927706f2543Smrg case AccelProfileNone: 928706f2543Smrg return NoProfile; 929706f2543Smrg default: 930706f2543Smrg return NULL; 931706f2543Smrg } 932706f2543Smrg} 933706f2543Smrg 934706f2543Smrg/** 935706f2543Smrg * Set the profile by number. 936706f2543Smrg * Intended to make profiles exchangeable at runtime. 937706f2543Smrg * If you created a profile, give it a number here and in the header to 938706f2543Smrg * make it selectable. In case some profile-specific init is needed, here 939706f2543Smrg * would be a good place, since FreeVelocityData() also calls this with 940706f2543Smrg * PROFILE_UNINITIALIZE. 941706f2543Smrg * 942706f2543Smrg * returns FALSE if profile number is unavailable, TRUE otherwise. 943706f2543Smrg */ 944706f2543Smrgint 945706f2543SmrgSetAccelerationProfile( 946706f2543Smrg DeviceVelocityPtr vel, 947706f2543Smrg int profile_num) 948706f2543Smrg{ 949706f2543Smrg PointerAccelerationProfileFunc profile; 950706f2543Smrg profile = GetAccelerationProfile(vel, profile_num); 951706f2543Smrg 952706f2543Smrg if(profile == NULL && profile_num != PROFILE_UNINITIALIZE) 953706f2543Smrg return FALSE; 954706f2543Smrg 955706f2543Smrg /* Here one could free old profile-private data */ 956706f2543Smrg free(vel->profile_private); 957706f2543Smrg vel->profile_private = NULL; 958706f2543Smrg /* Here one could init profile-private data */ 959706f2543Smrg vel->Profile = profile; 960706f2543Smrg vel->statistics.profile_number = profile_num; 961706f2543Smrg return TRUE; 962706f2543Smrg} 963706f2543Smrg 964706f2543Smrg/********************************************** 965706f2543Smrg * driver interaction 966706f2543Smrg **********************************************/ 967706f2543Smrg 968706f2543Smrg 969706f2543Smrg/** 970706f2543Smrg * device-specific profile 971706f2543Smrg * 972706f2543Smrg * The device-specific profile is intended as a hook for a driver 973706f2543Smrg * which may want to provide an own acceleration profile. 974706f2543Smrg * It should not rely on profile-private data, instead 975706f2543Smrg * it should do init/uninit in the driver (ie. with DEVICE_INIT and friends). 976706f2543Smrg * Users may override or choose it. 977706f2543Smrg */ 978706f2543Smrgvoid 979706f2543SmrgSetDeviceSpecificAccelerationProfile( 980706f2543Smrg DeviceVelocityPtr vel, 981706f2543Smrg PointerAccelerationProfileFunc profile) 982706f2543Smrg{ 983706f2543Smrg if(vel) 984706f2543Smrg vel->deviceSpecificProfile = profile; 985706f2543Smrg} 986706f2543Smrg 987706f2543Smrg/** 988706f2543Smrg * Use this function to obtain a DeviceVelocityPtr for a device. Will return NULL if 989706f2543Smrg * the predictable acceleration scheme is not in effect. 990706f2543Smrg */ 991706f2543SmrgDeviceVelocityPtr 992706f2543SmrgGetDevicePredictableAccelData( 993706f2543Smrg DeviceIntPtr dev) 994706f2543Smrg{ 995706f2543Smrg /*sanity check*/ 996706f2543Smrg if(!dev){ 997706f2543Smrg ErrorF("[dix] accel: DeviceIntPtr was NULL"); 998706f2543Smrg return NULL; 999706f2543Smrg } 1000706f2543Smrg if( dev->valuator && 1001706f2543Smrg dev->valuator->accelScheme.AccelSchemeProc == 1002706f2543Smrg acceleratePointerPredictable && 1003706f2543Smrg dev->valuator->accelScheme.accelData != NULL){ 1004706f2543Smrg 1005706f2543Smrg return (DeviceVelocityPtr)dev->valuator->accelScheme.accelData; 1006706f2543Smrg } 1007706f2543Smrg return NULL; 1008706f2543Smrg} 1009706f2543Smrg 1010706f2543Smrg/******************************** 1011706f2543Smrg * acceleration schemes 1012706f2543Smrg *******************************/ 1013706f2543Smrg 1014706f2543Smrg/** 1015706f2543Smrg * Modifies valuators in-place. 1016706f2543Smrg * This version employs a velocity approximation algorithm to 1017706f2543Smrg * enable fine-grained predictable acceleration profiles. 1018706f2543Smrg */ 1019706f2543Smrgvoid 1020706f2543SmrgacceleratePointerPredictable( 1021706f2543Smrg DeviceIntPtr dev, 1022706f2543Smrg int first_valuator, 1023706f2543Smrg int num_valuators, 1024706f2543Smrg int *valuators, 1025706f2543Smrg int evtime) 1026706f2543Smrg{ 1027706f2543Smrg float mult = 0.0; 1028706f2543Smrg int dx = 0, dy = 0; 1029706f2543Smrg int *px = NULL, *py = NULL; 1030706f2543Smrg DeviceVelocityPtr velocitydata = 1031706f2543Smrg (DeviceVelocityPtr) dev->valuator->accelScheme.accelData; 1032706f2543Smrg float fdx, fdy, tmp; /* no need to init */ 1033706f2543Smrg Bool soften = TRUE; 1034706f2543Smrg 1035706f2543Smrg if (!num_valuators || !valuators || !velocitydata) 1036706f2543Smrg return; 1037706f2543Smrg 1038706f2543Smrg if (velocitydata->statistics.profile_number == AccelProfileNone && 1039706f2543Smrg velocitydata->const_acceleration == 1.0f) { 1040706f2543Smrg return; /*we're inactive anyway, so skip the whole thing.*/ 1041706f2543Smrg } 1042706f2543Smrg 1043706f2543Smrg if (first_valuator == 0) { 1044706f2543Smrg dx = valuators[0]; 1045706f2543Smrg px = &valuators[0]; 1046706f2543Smrg } 1047706f2543Smrg if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { 1048706f2543Smrg dy = valuators[1 - first_valuator]; 1049706f2543Smrg py = &valuators[1 - first_valuator]; 1050706f2543Smrg } 1051706f2543Smrg 1052706f2543Smrg if (dx || dy){ 1053706f2543Smrg /* reset non-visible state? */ 1054706f2543Smrg if (ProcessVelocityData2D(velocitydata, dx , dy, evtime)) { 1055706f2543Smrg soften = FALSE; 1056706f2543Smrg } 1057706f2543Smrg 1058706f2543Smrg if (dev->ptrfeed && dev->ptrfeed->ctrl.num) { 1059706f2543Smrg /* invoke acceleration profile to determine acceleration */ 1060706f2543Smrg mult = ComputeAcceleration (dev, velocitydata, 1061706f2543Smrg dev->ptrfeed->ctrl.threshold, 1062706f2543Smrg (float)dev->ptrfeed->ctrl.num / 1063706f2543Smrg (float)dev->ptrfeed->ctrl.den); 1064706f2543Smrg 1065706f2543Smrg if(mult != 1.0 || velocitydata->const_acceleration != 1.0) { 1066706f2543Smrg ApplySofteningAndConstantDeceleration( velocitydata, 1067706f2543Smrg dx, dy, 1068706f2543Smrg &fdx, &fdy, 1069706f2543Smrg (mult > 1.0) && soften); 1070706f2543Smrg 1071706f2543Smrg if (dx) { 1072706f2543Smrg tmp = mult * fdx + dev->last.remainder[0]; 1073706f2543Smrg /* Since it may not be apparent: lrintf() does not offer 1074706f2543Smrg * strong statements about rounding; however because we 1075706f2543Smrg * process each axis conditionally, there's no danger 1076706f2543Smrg * of a toggling remainder. Its lack of guarantees likely 1077706f2543Smrg * makes it faster on the average target. */ 1078706f2543Smrg *px = lrintf(tmp); 1079706f2543Smrg dev->last.remainder[0] = tmp - (float)*px; 1080706f2543Smrg } 1081706f2543Smrg if (dy) { 1082706f2543Smrg tmp = mult * fdy + dev->last.remainder[1]; 1083706f2543Smrg *py = lrintf(tmp); 1084706f2543Smrg dev->last.remainder[1] = tmp - (float)*py; 1085706f2543Smrg } 1086706f2543Smrg DebugAccelF("pos (%i | %i) remainders x: %.3f y: %.3f delta x:%.3f y:%.3f\n", 1087706f2543Smrg *px, *py, dev->last.remainder[0], dev->last.remainder[1], fdx, fdy); 1088706f2543Smrg } 1089706f2543Smrg } 1090706f2543Smrg } 1091706f2543Smrg /* remember last motion delta (for softening/slow movement treatment) */ 1092706f2543Smrg velocitydata->last_dx = dx; 1093706f2543Smrg velocitydata->last_dy = dy; 1094706f2543Smrg} 1095706f2543Smrg 1096706f2543Smrg 1097706f2543Smrg 1098706f2543Smrg/** 1099706f2543Smrg * Originally a part of xf86PostMotionEvent; modifies valuators 1100706f2543Smrg * in-place. Retained mostly for embedded scenarios. 1101706f2543Smrg */ 1102706f2543Smrgvoid 1103706f2543SmrgacceleratePointerLightweight( 1104706f2543Smrg DeviceIntPtr dev, 1105706f2543Smrg int first_valuator, 1106706f2543Smrg int num_valuators, 1107706f2543Smrg int *valuators, 1108706f2543Smrg int ignored) 1109706f2543Smrg{ 1110706f2543Smrg float mult = 0.0; 1111706f2543Smrg int dx = 0, dy = 0; 1112706f2543Smrg int *px = NULL, *py = NULL; 1113706f2543Smrg 1114706f2543Smrg if (!num_valuators || !valuators) 1115706f2543Smrg return; 1116706f2543Smrg 1117706f2543Smrg if (first_valuator == 0) { 1118706f2543Smrg dx = valuators[0]; 1119706f2543Smrg px = &valuators[0]; 1120706f2543Smrg } 1121706f2543Smrg if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { 1122706f2543Smrg dy = valuators[1 - first_valuator]; 1123706f2543Smrg py = &valuators[1 - first_valuator]; 1124706f2543Smrg } 1125706f2543Smrg 1126706f2543Smrg if (!dx && !dy) 1127706f2543Smrg return; 1128706f2543Smrg 1129706f2543Smrg if (dev->ptrfeed && dev->ptrfeed->ctrl.num) { 1130706f2543Smrg /* modeled from xf86Events.c */ 1131706f2543Smrg if (dev->ptrfeed->ctrl.threshold) { 1132706f2543Smrg if ((abs(dx) + abs(dy)) >= dev->ptrfeed->ctrl.threshold) { 1133706f2543Smrg dev->last.remainder[0] = ((float)dx * 1134706f2543Smrg (float)(dev->ptrfeed->ctrl.num)) / 1135706f2543Smrg (float)(dev->ptrfeed->ctrl.den) + 1136706f2543Smrg dev->last.remainder[0]; 1137706f2543Smrg if (px) { 1138706f2543Smrg *px = (int)dev->last.remainder[0]; 1139706f2543Smrg dev->last.remainder[0] = dev->last.remainder[0] - 1140706f2543Smrg (float)(*px); 1141706f2543Smrg } 1142706f2543Smrg 1143706f2543Smrg dev->last.remainder[1] = ((float)dy * 1144706f2543Smrg (float)(dev->ptrfeed->ctrl.num)) / 1145706f2543Smrg (float)(dev->ptrfeed->ctrl.den) + 1146706f2543Smrg dev->last.remainder[1]; 1147706f2543Smrg if (py) { 1148706f2543Smrg *py = (int)dev->last.remainder[1]; 1149706f2543Smrg dev->last.remainder[1] = dev->last.remainder[1] - 1150706f2543Smrg (float)(*py); 1151706f2543Smrg } 1152706f2543Smrg } 1153706f2543Smrg } 1154706f2543Smrg else { 1155706f2543Smrg mult = pow((float)dx * (float)dx + (float)dy * (float)dy, 1156706f2543Smrg ((float)(dev->ptrfeed->ctrl.num) / 1157706f2543Smrg (float)(dev->ptrfeed->ctrl.den) - 1.0) / 1158706f2543Smrg 2.0) / 2.0; 1159706f2543Smrg if (dx) { 1160706f2543Smrg dev->last.remainder[0] = mult * (float)dx + 1161706f2543Smrg dev->last.remainder[0]; 1162706f2543Smrg *px = (int)dev->last.remainder[0]; 1163706f2543Smrg dev->last.remainder[0] = dev->last.remainder[0] - 1164706f2543Smrg (float)(*px); 1165706f2543Smrg } 1166706f2543Smrg if (dy) { 1167706f2543Smrg dev->last.remainder[1] = mult * (float)dy + 1168706f2543Smrg dev->last.remainder[1]; 1169706f2543Smrg *py = (int)dev->last.remainder[1]; 1170706f2543Smrg dev->last.remainder[1] = dev->last.remainder[1] - 1171706f2543Smrg (float)(*py); 1172706f2543Smrg } 1173706f2543Smrg } 1174706f2543Smrg } 1175706f2543Smrg} 1176