# On branch master # Changes to be committed: # (use "git reset HEAD ..." to unstage) # # modified: Xi/getdctl.c # modified: dix/Makefile.am # modified: dix/devices.c # modified: dix/getevents.c # new file: dix/ptrveloc.c # modified: hw/xfree86/common/xf86Xinput.c # modified: include/input.h # modified: include/inputstr.h # new file: include/ptrveloc.h # # Untracked files: # (use "git add ..." to include in what will be committed) # # dix/devices.loT diff --git a/Xi/getdctl.c b/Xi/getdctl.c index 7175dc2..ba8f338 100644 --- a/Xi/getdctl.c +++ b/Xi/getdctl.c @@ -212,6 +212,30 @@ static void CopySwapDeviceEnable (ClientPtr client, DeviceIntPtr dev, char *buf) } } +static void +CopySwapDeviceAccel (ClientPtr client, + DeviceIntPtr dev, + char *buf) +{ + char n; + xDeviceAccelerationState *e = (xDeviceAccelerationState*) buf; + + e->control = DEVICE_PTRACCEL; + e->length = sizeof(xDeviceAccelerationState); + e->operation + + + if (client->swapped) { + swaps(&e->control, n); + swaps(&e->length, n); + swapl(&e->operation, n); + swapl(&e->integer_val, n); + swapl(&e->rational_nom, n); + swapl(&e->rational_den, n); + } +} + + /*********************************************************************** * * This procedure writes the reply for the xGetDeviceControl function, @@ -280,6 +304,14 @@ ProcXGetDeviceControl(ClientPtr client) case DEVICE_ENABLE: total_length = sizeof(xDeviceEnableCtl); break; + case DEVICE_PTRACCEL: + if (!dev->valuator /* || !dev->valuator TODO add check on acc enabled*/) { + SendErrorToClient(client, IReqCode, X_GetDeviceControl, 0, + BadMatch); + return Success; + } + total_length = sizeof(xDeviceAccelerationState /*TODO xState or xCtl same anyway but what is regarded correct ? */); + break; default: return BadValue; } @@ -305,6 +337,9 @@ ProcXGetDeviceControl(ClientPtr client) case DEVICE_ENABLE: CopySwapDeviceEnable(client, dev, buf); break; + case DEVICE_PTRACCEL: + CopySwapDeviceAccel(client, dev, buf); + break; default: break; } diff --git a/dix/Makefile.am b/dix/Makefile.am index 28c2d8b..10b45e1 100644 --- a/dix/Makefile.am +++ b/dix/Makefile.am @@ -26,6 +26,7 @@ libdix_la_SOURCES = \ pixmap.c \ privates.c \ property.c \ + ptrveloc.c \ resource.c \ swaprep.c \ swapreq.c \ diff --git a/dix/devices.c b/dix/devices.c index 3855c2b..72bac48 100644 --- a/dix/devices.c +++ b/dix/devices.c @@ -62,6 +62,7 @@ SOFTWARE. #include "scrnintstr.h" #include "cursorstr.h" #include "dixstruct.h" +#include "ptrveloc.h" #include "site.h" #ifndef XKB_IN_SERVER #define XKB_IN_SERVER @@ -198,6 +199,10 @@ EnableDevice(DeviceIntPtr dev) ; *prev = dev; dev->next = NULL; + + /*if(dev->valuator && dev->valuator->AccelInitProc){ + dev->valuator->AccelInitProc(dev); + }*/ ev.type = DevicePresenceNotify; ev.time = currentTime.milliseconds; @@ -535,6 +540,10 @@ CloseDevice(DeviceIntPtr dev) } if (dev->valuator) { + /* free pointer acceleration stuff */ + if(dev->valuator->AccelCleanupProc != NULL){ + dev->valuator->AccelCleanupProc(dev); + } /* Counterpart to 'biggest hack ever' in init. */ if (dev->valuator->motion && dev->valuator->GetMotionProc == GetMotionHistory) @@ -944,6 +953,8 @@ InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, valc->dxremaind = 0; valc->dyremaind = 0; dev->valuator = valc; + /*InitPointerAccelerationScheme(dev, NULL, NULL, NULL);*/ /* more clean */ + InitDefaultPointerAccelerationScheme(dev); /* easier for hotplug devs */ /* biggest hack ever. */ if (motionProc == GetMotionHistory) @@ -957,6 +968,31 @@ InitValuatorClassDeviceStruct(DeviceIntPtr dev, int numAxes, } _X_EXPORT Bool +InitPointerAccelerationScheme(DeviceIntPtr dev, + PointerAccelSchemeProcPtr schemeProc, + void* schemeData, + CleanupAccelSchemeProcPtr cleanupProc) +{ + ValuatorClassPtr val = dev->valuator; + val->AccelSchemeProc = schemeProc; + val->AccelCleanupProc = cleanupProc; + val->accelData = schemeData; + return TRUE; +} + +_X_EXPORT Bool +InitDefaultPointerAccelerationScheme(DeviceIntPtr dev) +{ + DeviceVelocityPtr s; + s = xalloc(sizeof(DeviceVelocityData)); + InitVelocityData(s); + + return InitPointerAccelerationScheme(dev, acceleratePointer, s, + AccelerationDefaultCleanup); +} + + +_X_EXPORT Bool InitAbsoluteClassDeviceStruct(DeviceIntPtr dev) { AbsoluteClassPtr abs; diff --git a/dix/getevents.c b/dix/getevents.c index 3754c72..24b2144 100644 --- a/dix/getevents.c +++ b/dix/getevents.c @@ -62,6 +62,7 @@ extern Bool XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst, Bool sendNotifies); #include "exglobals.h" #include "extnsionst.h" +//#include /* Maximum number of valuators, divided by six, rounded up, to get number * of events. */ @@ -70,6 +71,7 @@ extern Bool XkbCopyKeymap(XkbDescPtr src, XkbDescPtr dst, Bool sendNotifies); /* Number of motion history events to store. */ #define MOTION_HISTORY_SIZE 256 +/*#include */ /** * Pick some arbitrary size for Xi motion history. @@ -225,80 +227,6 @@ GetMaximumEventsNum(void) { } -/* Originally a part of xf86PostMotionEvent; modifies valuators - * in-place. */ -static void -acceleratePointer(DeviceIntPtr pDev, int first_valuator, int num_valuators, - int *valuators) -{ - float mult = 0.0; - int dx = 0, dy = 0; - int *px = NULL, *py = NULL; - - if (!num_valuators || !valuators) - return; - - if (first_valuator == 0) { - dx = valuators[0]; - px = &valuators[0]; - } - if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { - dy = valuators[1 - first_valuator]; - py = &valuators[1 - first_valuator]; - } - - if (!dx && !dy) - return; - - if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) { - /* modeled from xf86Events.c */ - if (pDev->ptrfeed->ctrl.threshold) { - if ((abs(dx) + abs(dy)) >= pDev->ptrfeed->ctrl.threshold) { - pDev->valuator->dxremaind = ((float)dx * - (float)(pDev->ptrfeed->ctrl.num)) / - (float)(pDev->ptrfeed->ctrl.den) + - pDev->valuator->dxremaind; - if (px) { - *px = (int)pDev->valuator->dxremaind; - pDev->valuator->dxremaind = pDev->valuator->dxremaind - - (float)(*px); - } - - pDev->valuator->dyremaind = ((float)dy * - (float)(pDev->ptrfeed->ctrl.num)) / - (float)(pDev->ptrfeed->ctrl.den) + - pDev->valuator->dyremaind; - if (py) { - *py = (int)pDev->valuator->dyremaind; - pDev->valuator->dyremaind = pDev->valuator->dyremaind - - (float)(*py); - } - } - } - else { - mult = pow((float)dx * (float)dx + (float)dy * (float)dy, - ((float)(pDev->ptrfeed->ctrl.num) / - (float)(pDev->ptrfeed->ctrl.den) - 1.0) / - 2.0) / 2.0; - if (dx) { - pDev->valuator->dxremaind = mult * (float)dx + - pDev->valuator->dxremaind; - *px = (int)pDev->valuator->dxremaind; - pDev->valuator->dxremaind = pDev->valuator->dxremaind - - (float)(*px); - } - if (dy) { - pDev->valuator->dyremaind = mult * (float)dy + - pDev->valuator->dyremaind; - *py = (int)pDev->valuator->dyremaind; - pDev->valuator->dyremaind = pDev->valuator->dyremaind - - (float)(*py); - } - } - } -} - - /** * Clip an axis to its bounds, which are declared in the call to * InitValuatorAxisClassStruct. @@ -591,9 +519,13 @@ GetPointerEvents(xEvent *events, DeviceIntPtr pDev, int type, int buttons, } } else { - if (flags & POINTER_ACCELERATE) - acceleratePointer(pDev, first_valuator, num_valuators, - valuators); + if ((flags & POINTER_ACCELERATE) && pDev->valuator->AccelSchemeProc){ + pDev->valuator->AccelSchemeProc(pDev, + first_valuator, + num_valuators, + valuators, + ms); + } if (pDev->coreEvents) { if (first_valuator == 0 && num_valuators >= 1) diff --git a/dix/ptrveloc.c b/dix/ptrveloc.c new file mode 100644 index 0000000..6377e72 --- /dev/null +++ b/dix/ptrveloc.c @@ -0,0 +1,719 @@ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include +#include +#include +#include + + +//needed fwds +void +AccelerationDefaultCleanup(DeviceIntPtr pDev); + +void +acceleratePointer(DeviceIntPtr pDev, int first_valuator, int num_valuators, + int *valuators, int evtime); + +/***************************************************************************** + * Pointer ballistics 2006/2007 by Simon Thum (simon [dot] thum [at] gmx de) + * + * Serves 3 complementary functions: + * 1) provide a sophisticated ballistic velocity estimate to improve + * the relation between velocity (of the device) and acceleration + * 2) slightly soften mouse data when acceleration is applied + * 3) decelerate by two means (constant and adaptive) if enabled + * + * Important concepts are the + * + * - Scheme + * which selects the basic algorithm (old/new (new is the stuff herein)) + * - Profile + * which selects or excutes the + * - Function, + * which returns an acceleration for any given velocity. + * + * The Profile is an intermediary intended to cleanly perform old-style + * function selection (threshold =/!= 0) + * However, custom functions should be implemented as a profile. + * A function can be identical with a profile, see below. + ****************************************************************************/ + + + + +/******************************** + * Init/Uninit etc + *******************************/ + +/** + * Init struct so it should match the average case + */ +void +InitVelocityData(DeviceVelocityPtr s) +{ + s->lrm_time = 0; + s->velocity = 0; + s->corr_mul = 10.0; /* dots per 10 milisecond should be usable */ + s->const_acceleration = 1.0; /* no acceleration/deceleration */ + s->reset_time = 300; + s->last_dx = 0; + s->last_dy = 0; + s->use_softening = 1; + s->min_acceleration = 1.0; /* don't decelerate */ + s->use_coupling = 1; + s->coupling = 0.2; + s->fading_lut_size = 0; + s->fading_lut = NULL; + s->profile_private = NULL; + SetAccelerationProfile(s, 0); + /* init, but don't do a LUT since we probably create one afterwards */ + InitFadingLookup(s, 15, 0); +} + +/** + * Clean up + * param is a void* since the ptr in ValuatorClassRec is not typed either + */ +void +FreeVelocityData(void* p){ + DeviceVelocityPtr s = (DeviceVelocityPtr)p; + s->fading_lut_size = 0; + xfree(s->fading_lut); + s->fading_lut = NULL; + xfree(s->profile_private); + s->profile_private = NULL; +} + +/** + * Adjust weighting decay and lut in sync + * The fn is designed so its integral 0->inf is unity, so we end + * up with a stable (basically IIR) filter. It always draws + * towards its more current input values, which have more weight the older + * the last input value is. + */ +void +InitFadingLookup(DeviceVelocityPtr s, float rdecay, int lutsize) +{ + int x; + float *newlut; + float *oldlut; + + if(lutsize > 0){ + newlut = xalloc (sizeof(float)* lutsize); + if(!newlut) + return; + for(x = 0; x < lutsize; x++) + newlut[x] = pow(0.5, ((float)x) * rdecay); + }else{ + newlut = NULL; + } + oldlut = s->fading_lut; + s->fading_lut = newlut; + s->rdecay = rdecay; + s->fading_lut_size = lutsize; + if(oldlut != NULL) + xfree(oldlut); +} + + +/************************************* + * dix/ddx init/uninit coordination + ************************************/ +/*DeviceVelocityPtr InitImprovedAccelerationScheme(){ + DeviceVelocityPtr pVel = xalloc(sizeof(DeviceVelocityPtr)); + InitVelocityData (pVel); + return pVel; +}*/ + +void +AccelerationDefaultCleanup(DeviceIntPtr pDev){ + if( pDev->valuator->AccelSchemeProc == acceleratePointer /*sanity check*/ + && pDev->valuator->accelData != NULL){ + FreeVelocityData(pDev->valuator->accelData); + xfree(pDev->valuator->accelData); + pDev->valuator->accelData = NULL; + } +} + +/************************************* + * Get/ChangeDeviceControl + ************************************/ + +/* Cheatsheet + +#define DEVICE_PTRACCEL 100 + +#define DevicePtrAccel_Scheme (DEVICE_PTRACCEL + 0) +#define DevicePtrAccel_Profile (DEVICE_PTRACCEL + 1) +#define DevicePtrAccel_Scale (DEVICE_PTRACCEL + 2) +#define DevicePtrAccel_Coeff (DEVICE_PTRACCEL + 3) +#define DevicePtrAccel_AvgDecay (DEVICE_PTRACCEL + 4) +#define DevicePtrAccel_MinAcc (DEVICE_PTRACCEL + 5) +#define DevicePtrAccel_ConstAcc (DEVICE_PTRACCEL + 6) +#define DevicePtrAccel_Soften (DEVICE_PTRACCEL + 7) +#define DevicePtrAccel_Coupling (DEVICE_PTRACCEL + 8) +#define DevicePtrAccel_LutSize (DEVICE_PTRACCEL + 9) +#define DevicePtrAccel_SampleAcceleration (DEVICE_PTRACCEL + 10) +#define DevicePtrAccel_SampleVelocity (DEVICE_PTRACCEL + 11) + +typedef struct { + XID control; + int length; + int operation; + int integer_val; + int rational_nom; + int rational_den; +} XDeviceAccelerationControl; +*/ + +//auto-find denominator? +static void FloatToControl(xDeviceAccelerationCtl* ctl, float val, int den){ + ctl->rational_nom = val * den; + ctl->rational_den = den; +} + +static float ControlToFloat(xDeviceAccelerationCtl* ctl, Bool* success){ + *success = (ctl->rational_den != 0); + if(success) + return ((float)ctl->rational_nom / ctl->rational_den); + else + return 0; +} + +Bool HandleRequestGetAccelDevCtrl(DeviceIntPtr pDev, + xDeviceAccelerationCtl* ctl) +{ + if(ctl->control != DEVICE_PTRACCEL) + return FALSE; + if(pDev->valuator->AccelSchemeProc != acceleratePointer) + return FALSE; + DeviceVelocityPtr pVel = (DeviceVelocityPtr)pDev->valuator->accelData; + + switch(ctl->operation){ + case DevicePtrAccel_Scale: + FloatToControl(ctl, pVel->corr_mul, 1000); + break; + default: + return FALSE; + } + return TRUE; +} + + +Bool HandleRequestChangeAccelDevCtrl(DeviceIntPtr pDev, + xDeviceAccelerationCtl* ctl) +{ + if(ctl->control != DEVICE_PTRACCEL) + return FALSE; + if(pDev->valuator->AccelSchemeProc != acceleratePointer) + return FALSE; + DeviceVelocityPtr pVel = (DeviceVelocityPtr)pDev->valuator->accelData; + Bool p_rational_success; + float p_rational = ControlToFloat(ctl, &p_rational_success); + + switch(ctl->operation){ + case DevicePtrAccel_Scale: + if(p_rational > 0){ + pVel->corr_mul = p_rational; + return TRUE; + } + break; + default: + return FALSE; + } + return FALSE; +} + + +/******************************** + * general Velocity and + * acceleration computation + *******************************/ + +/** + * return the axis if mickey is insignificant and axis-aligned + * or -1 otherwise + * 1 for x-axis + * 2 for y-axis + */ +static inline short +GetAxis(int dx, int dy){ + if(dx == 0 || dy == 0){ + if(dx == 1 || dx == -1) + return 1; + if(dy == 1 || dy == -1) + return 2; + return -1; + }else{ + return -1; + } +} + +/** + * Perform velocity approximation + * return true if non-visible state reset is suggested + */ +static short +ProcessVelocityData(DeviceVelocityPtr s, int dx, int dy, int time) +{ + int diff = time - s->lrm_time; + int cur_ax = GetAxis(dx, dy); + int last_ax = GetAxis(s->last_dx, s->last_dy); + float cvelocity, fade, div; + short reset = (diff >= s->reset_time); + if(cur_ax != last_ax && cur_ax != -1 && last_ax != -1 && !reset){ + /* correct for the error induced when diagonal movements are + reported as alternating axis mickeys */ + dx += s->last_dx; + dy += s->last_dy; + diff += s->last_diff; + s->last_diff = time - s->lrm_time; /* prevent continuous add-up */ + }else{ + s->last_diff = diff; + } + + /** + * cvelocity is not a real velocity yet, more a motion delta. contant + * acceleration is multiplied here to make the velocity an on-screen + * velocity (px/t as opposed to some device velocity). This is intended to + * make multiple devices with widely varying ConstantDecelerations behave + * more similar. + */ + cvelocity = (float)sqrt(dx*dx + dy*dy) * s->const_acceleration; + + s->lrm_time = time; + + if (s->reset_time < 0 || diff < 0) { /* disabled or timer overrun? */ + /* simply set velocity from current movement, no reset. */ + s->velocity = cvelocity; + return 0; + } + + if (reset) { + /* no movement for some time, suggest to reset nonvisible state */ + diff = s->reset_time; + reset = TRUE; + } + + if (diff == 0) + diff = 1; /* prevent div-by-zero, though it shouldn't happen anyway*/ + + /* translate velocity to dots/ms (somewhat untractable in integers, + so we multiply by some per-device adjustable factor) */ + cvelocity = cvelocity * s->corr_mul / (float)diff; + + /* short-circuit: when nv-reset the rest can be skipped */ + if(reset == TRUE){ + s->velocity = cvelocity; + return TRUE; + } + + /* fading factor; crucial it matches definition in InitFadingLookup */ + if(diff < s->fading_lut_size) + fade = s->fading_lut[diff]; + else + fade = pow(0.5, ((float)diff) * s->rdecay); + + s->velocity *= fade; /* fade out old velocity info */ + s->velocity += cvelocity * (1.0f - fade); /* and add up current */ + + /* perform coupling */ + if(s->use_coupling && cvelocity > 0 && fabs(s->velocity-cvelocity) > 1.0){ + div = fabs(cvelocity - s->velocity) / (s->velocity + cvelocity); + if(div > s->coupling){ /* if divergence is too high... */ + /* set current velocity as weighted to catch up quickly */ + s->velocity = cvelocity; + } + } + /*DebugF("ProcessVelocityData acceleration guessing: vel=%.3f + diff=%d curw=%.4f, fade=%.4f\n", s->velocity, diff, (1.0- fade), fade);*/ + return reset; +} + + +/** + * this flattens significant ( > 1) mickeys a little bit for more steady + * constant-velocity response + */ +static inline float +ApplySimpleSoftening(int od, int d) +{ + float res = d; + if (d <= 1 && d >= -1) + return res; + if (d > od) + res -= 0.5; + else if (d < od) + res += 0.5; + return res; +} + + +static void +ApplySofteningAndConstantDeceleration( + DeviceVelocityPtr s, + int dx, + int dy, + float* fdx, + float* fdy, + short do_soften) +{ + if (do_soften && s->use_softening) { + *fdx = ApplySimpleSoftening(s->last_dx, dx); + *fdy = ApplySimpleSoftening(s->last_dy, dy); + } else { + *fdx = dx; + *fdy = dy; + } + + *fdx *= s->const_acceleration; + *fdy *= s->const_acceleration; +} + + + +/***************************************** + * Acceleration functions and profiles + ****************************************/ + +/** + * acceleration function similar to classic accelerated/unaccelerated, + * introducing some transition range + */ +static float +SimpleAccelerationFunc(float velocity, float threshold, float acc) +{ + if(velocity < 1.0) + return velocity; + + if (velocity <= threshold) + return 1; + + velocity /= threshold; + + if (velocity >= acc) + return acc; + else + return velocity; + +} + +/** + * Polynomial function similar previous one, but with f(1) = 1 + */ +static float +PolynomialAccelerationFunc(float velocity, float ignored, float acc) +{ + return pow(velocity, (acc - 1.0) * 0.5); +} + +/** + * returns acceleration for velocity. + * This profile selects the two functions like the old scheme did + */ +static float +ClassicProfile( + DeviceVelocityPtr pVel, + float threshold, + float acc) +{ + + if (threshold) { + return SimpleAccelerationFunc (pVel->velocity, + threshold, + acc); + } else { + return PolynomialAccelerationFunc(pVel->velocity, + 0, + acc); + } +} + +/** + * Power profile + * This has a completely smooth transition curve, i.e. no jumps in the + * derivatives. + * + * This has the expense of overall response dependency on min-acceleration. + * In effect, min_acceleration mimics const_acceleration in this profile. + */ +static float +PowerProfile( + DeviceVelocityPtr pVel, + float threshold, + float acc) +{ + float vel_dist; + + acc /= 10.f; //without this, acc of 2 is unuseable + + if (pVel->velocity <= threshold) + return pVel->min_acceleration; + vel_dist = pVel->velocity - threshold; + return (pow(acc, vel_dist)) * pVel->min_acceleration; +} + + +/** + * just a smooth function in [0..1] -> [0..1] + * - point-symmetric at 0.5 + * - f'(0) = f'(1) = 0 + */ +static float +CalcNaturalGradient(float x){ + x *= 2.0f; + x -= 1.0f; + return 0.5f + (x * sqrt(1.0f - x*x) + asin(x))/M_PI; +} + +/** + * acceleration function similar to classic accelerated/unaccelerated, + * but with smooth transition in between (and towards zero for adaptive dec.). + */ +static float +SimpleSmoothProfile( + DeviceVelocityPtr pVel, + float threshold, + float acc) +{ + float velocity = pVel->velocity; + if(velocity < 1.0f) + return CalcNaturalGradient(0.5 + velocity*0.5) * 2.0f - 1.0f; + if(threshold < 1.0f) + threshold = 1.0f; + if (velocity <= threshold) + return 1; + velocity /= threshold; + if (velocity >= acc) + return acc; + else + return 1.0f + (CalcNaturalGradient(velocity/acc) * (acc - 1.0f)); +} + +/** + * This profile uses the first half of the narural gradient as a start + * and then scales linearly. might not bear f(1) = 1 + */ +static float +SmoothLinearProfile( + DeviceVelocityPtr pVel, + float threshold, + float acc) +{ + if(acc > 1.0f) + acc -= 1.0f; /*this is so acc = 1 is no acceleration */ + else + return 1.0f; /* originally X just went stupid, here we disable */ + + float nv = (pVel->velocity - threshold) * acc * 0.5f; + float res; + if(nv < 0){ + res = 0; + }else if(nv < 2){ + res = CalcNaturalGradient(nv*0.25f)*2.0f; + }else{ + nv -= 2.0f; + res = nv * 2.0f / M_PI /* steepness of gradient at 0.5 */ + + 1.0f; /* gradient crosses 2|1 */ + } + res += pVel->min_acceleration; + return res; +} + +/** + * Set the profile by number. + * Intended to make profiles exchangeable at runtime. + * If you created a profile, give it a number here to make it selectable. + * In case some profile-specific init is needed, here would be a good place. + * returns FALSE (0) if profile number is unknown. + */ +int +SetAccelerationProfile( + DeviceVelocityPtr s, + int profile_num) +{ + PointerAccelerationProfileFunc profile; + switch(profile_num){ + default: + return FALSE; + case 0: + profile = ClassicProfile; + break; + case 1: + profile = SmoothLinearProfile; + break; + case 2: + profile = SimpleSmoothProfile; + break; + case 3: + profile = PowerProfile; + break; + } + if(s->profile_private != NULL){ + /*Here one could free old profile data*/ + xfree(s->profile_private); + s->profile_private = NULL; + } + /*Here one could init profile-private data*/ + s->Profile = profile; + return TRUE; +} + + + + +/******************************** + * acceleration schemes + *******************************/ + +/** + * Modifies valuators in-place. + * This version employs a velocity approximation algorithm to + * enable fine-grained acceleration profiles. + */ +void +acceleratePointer(DeviceIntPtr pDev, int first_valuator, int num_valuators, + int *valuators, int evtime) +{ + float mult = 0.0; + int dx = 0, dy = 0; + int *px = NULL, *py = NULL; + DeviceVelocityPtr velocitydata = (DeviceVelocityPtr) pDev->valuator->accelData; + float fdx, fdy; /* no need to init */ + + if (!num_valuators || !valuators || !velocitydata) + return; + + if (first_valuator == 0) { + dx = valuators[0]; + px = &valuators[0]; + } + if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { + dy = valuators[1 - first_valuator]; + py = &valuators[1 - first_valuator]; + } + + if (dx || dy){ + /* reset nonvisible state? */ + if (ProcessVelocityData(velocitydata, dx , dy, evtime)) { + /* set to center of pixel */ + pDev->valuator->dxremaind = pDev->valuator->dyremaind = 0.5f; + /* prevent softening (somewhat quirky solution, + as it depends on the algorithm) */ + velocitydata->last_dx = dx; + velocitydata->last_dy = dy; + } + + if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) { + /* invoke acceleration profile to determine acceleration */ + mult = velocitydata->Profile(velocitydata, + pDev->ptrfeed->ctrl.threshold, + (float)(pDev->ptrfeed->ctrl.num) / + (float)(pDev->ptrfeed->ctrl.den)); + + /* enforce min_acceleration */ + if (mult < velocitydata->min_acceleration) + mult = velocitydata->min_acceleration; + + if(mult != 1.0 || velocitydata->const_acceleration != 1.0) { + ApplySofteningAndConstantDeceleration( velocitydata, + dx, dy, + &fdx, &fdy, + mult > 1.0); + if (dx) { + pDev->valuator->dxremaind = mult * fdx + pDev->valuator->dxremaind; + *px = (int)pDev->valuator->dxremaind; + pDev->valuator->dxremaind = pDev->valuator->dxremaind - (float)*px; + } + if (dy) { + pDev->valuator->dyremaind = mult * fdy + pDev->valuator->dyremaind; + *py = (int)pDev->valuator->dyremaind; + pDev->valuator->dyremaind = pDev->valuator->dyremaind - (float)*py; + } + } + } + } + /* remember last motion delta (for softening/slow movement treatment) */ + velocitydata->last_dx = dx; + velocitydata->last_dy = dy; +} + + + +/** + * Originally a part of xf86PostMotionEvent; modifies valuators + * in-place. + */ +void +acceleratePointerPlain(DeviceIntPtr pDev, int first_valuator, int num_valuators, + int *valuators, int ignore) +{ + float mult = 0.0; + int dx = 0, dy = 0; + int *px = NULL, *py = NULL; + + if (!num_valuators || !valuators) + return; + + if (first_valuator == 0) { + dx = valuators[0]; + px = &valuators[0]; + } + if (first_valuator <= 1 && num_valuators >= (2 - first_valuator)) { + dy = valuators[1 - first_valuator]; + py = &valuators[1 - first_valuator]; + } + + if (!dx && !dy) + return; + + if (pDev->ptrfeed && pDev->ptrfeed->ctrl.num) { + /* modeled from xf86Events.c */ + if (pDev->ptrfeed->ctrl.threshold) { + if ((abs(dx) + abs(dy)) >= pDev->ptrfeed->ctrl.threshold) { + pDev->valuator->dxremaind = ((float)dx * + (float)(pDev->ptrfeed->ctrl.num)) / + (float)(pDev->ptrfeed->ctrl.den) + + pDev->valuator->dxremaind; + if (px) { + *px = (int)pDev->valuator->dxremaind; + pDev->valuator->dxremaind = pDev->valuator->dxremaind - + (float)(*px); + } + + pDev->valuator->dyremaind = ((float)dy * + (float)(pDev->ptrfeed->ctrl.num)) / + (float)(pDev->ptrfeed->ctrl.den) + + pDev->valuator->dyremaind; + if (py) { + *py = (int)pDev->valuator->dyremaind; + pDev->valuator->dyremaind = pDev->valuator->dyremaind - + (float)(*py); + } + } + } + else { + mult = pow((float)(dx * dx + dy * dy), + ((float)(pDev->ptrfeed->ctrl.num) / + (float)(pDev->ptrfeed->ctrl.den) - 1.0) / + 2.0) / 2.0; + if (dx) { + pDev->valuator->dxremaind = mult * (float)dx + + pDev->valuator->dxremaind; + *px = (int)pDev->valuator->dxremaind; + pDev->valuator->dxremaind = pDev->valuator->dxremaind - + (float)(*px); + } + if (dy) { + pDev->valuator->dyremaind = mult * (float)dy + + pDev->valuator->dyremaind; + *py = (int)pDev->valuator->dyremaind; + pDev->valuator->dyremaind = pDev->valuator->dyremaind - + (float)(*py); + } + } + } +} diff --git a/hw/xfree86/common/xf86Xinput.c b/hw/xfree86/common/xf86Xinput.c index b939fb7..100c12b 100644 --- a/hw/xfree86/common/xf86Xinput.c +++ b/hw/xfree86/common/xf86Xinput.c @@ -87,12 +87,139 @@ #include "mi.h" +#include /* dix pointer acceleration */ + + #ifdef XFreeXDGA #include "dgaproc.h" #endif xEvent *xf86Events = NULL; + +/** + * Eval config and init MouseVelocityData accordingly + */ +static void +ProcessVelocityConfiguration(char* devname, pointer list, DeviceVelocityPtr s){ + int tempi; + float tempf; + + if(!s) + return; + + /* TODO These settings should be reachable by API/xset */ + tempf = xf86SetRealOption(list, "WeightingDecay", 15); + xf86Msg(X_CONFIG, "%s: weighting decay %.1f ms\n", devname, tempf); + if(tempf > 0) + tempf = 1.0 / tempf; /* set reciprocal if possible */ + else + tempf = 10000; /* else set fairly high */ + + InitFadingLookup(s, tempf, 40); + + tempf = xf86SetIntOption(list, "ConstantDeceleration", 1); + if(tempf > 1.0){ + xf86Msg(X_CONFIG, "%s: constant deceleration by %.1f\n", devname, + tempf); + s->const_acceleration = 1.0 / tempf; /* set reciprocal deceleration + alias acceleration */ + } + + tempf = xf86SetIntOption(list, "AdaptiveDeceleration", 1); + if(tempf > 1.0){ + xf86Msg(X_CONFIG, "%s: adaptive deceleration by %.1f\n", devname, + tempf); + s->min_acceleration = 1.0 / tempf; /* set minimum acceleration */ + } + + tempf = xf86SetRealOption(list, "VelocityCoupling", 0.2); + if(tempf > 0.0){ + xf86Msg(X_CONFIG, "%s: velocity coupling is %.1f%%\n", devname, + tempf*100.0); + s->coupling = tempf; + s->use_coupling = 1; + }else{ + s->use_coupling = 0; + xf86Msg(X_CONFIG, "%s: disabled velocity coupling\n", devname); + } + + /* Configure softening. If const deceleration is used, this is expected + * to provide better subpixel information so we enable + * softening by default only if ConstantDeceleration is not used + */ + s->use_softening = xf86SetBoolOption(list, "Softening", + s->const_acceleration == 1.0); + + s->reset_time = xf86SetIntOption(list, "VelocityReset", 300); + + tempf = xf86SetRealOption(list, "ExpectedRate", 0); + if(tempf > 0){ + s->corr_mul = 1000.0 / tempf; + }else{ + s->corr_mul = xf86SetRealOption(list, "VelocityScale", 10); + } + + if (xf86SetBoolOption(list, "EstimateVelocity", 1) == 0) { + s->reset_time = -1; /* Disable */ + s->use_softening = 0; + xf86Msg(X_CONFIG, "%s: disabled velocity estimation\n", devname); + } + + //select profile by number + tempi= xf86SetIntOption(list, "AccelerationProfile", 0); + if(SetAccelerationProfile(s, tempi)){ + xf86Msg(X_CONFIG, "%s: set acceleration profile %i\n", devname, tempi); + }else{ + xf86Msg(X_CONFIG, "%s: acceleration profile %i is unknown\n", + devname, tempi); + } +} + +static void +ApplyExtendedValuatorSettings(DeviceIntPtr dev){ + int scheme; + DeviceVelocityPtr pVel; + LocalDevicePtr local = (LocalDevicePtr)dev->public.devicePrivate; + xf86Msg(X_CONFIG, "%s: in ApplyExtendedValuatorSettings\n", local->name); + /* + * Clean out any (default) installed scheme and apply selected scheme. + */ + if(dev->valuator){ + /* first do cleanup */ + xf86Msg(X_CONFIG, "%s: before cleanup\n", local->name); + if(dev->valuator->AccelCleanupProc){ + dev->valuator->AccelCleanupProc(dev); + InitPointerAccelerationScheme(dev, NULL, NULL, NULL); + } + + scheme = xf86SetIntOption(local->options, "AccelerationScheme", 1); + xf86Msg(X_CONFIG, "%s: init scheme %i\n", local->name, scheme); + switch(scheme){ + case 0: + /* no-op: above cleanup does it */ + break; + case 1: + pVel = xalloc(sizeof(DeviceVelocityData)); + xf86Msg(X_CONFIG, "%s: IVD\n", local->name); + InitVelocityData (pVel); + xf86Msg(X_CONFIG, "%s: PVC\n", local->name); + ProcessVelocityConfiguration (local->name, local->options, + pVel); + xf86Msg(X_CONFIG, "%s: IPAS\n", local->name); + InitPointerAccelerationScheme(dev, acceleratePointer, + pVel, + AccelerationDefaultCleanup); + break; + case 2: + InitPointerAccelerationScheme(dev, acceleratePointerPlain, + NULL, NULL); + break; + } + } + xf86Msg(X_CONFIG, "%s: finished AEVS\n", local->name); +} + static Bool xf86SendDragEvents(DeviceIntPtr device) { @@ -131,6 +258,11 @@ xf86ProcessCommonOptions(LocalDevicePtr local, } else { xf86Msg(X_CONFIG, "%s: doesn't report drag events\n", local->name); } + /*xf86Msg(X_CONFIG, "%s: about to register accel init\n", local->name); + if(local->dev && local->dev->valuator){ + xf86Msg(X_CONFIG, "registering in xf86ProcessCommonOptions"); + local->dev->valuator->AccelInitProc = ApplyExtendedValuatorSettings; + }*/ /* Backwards compatibility. */ local->history_size = GetMotionHistorySize(); @@ -773,6 +905,11 @@ xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, int minval, int maxval, InitValuatorAxisStruct(dev, axnum, minval, maxval, resolution, min_res, max_res); + /*if(dev->valuator){ + xf86Msg(X_CONFIG, "registering in xf86InitValuatorAxisStruct"); + dev->valuator->AccelInitProc = ApplyExtendedValuatorSettings; + }*/ + ApplyExtendedValuatorSettings(dev); } /* @@ -782,6 +919,10 @@ xf86InitValuatorAxisStruct(DeviceIntPtr dev, int axnum, int minval, int maxval, _X_EXPORT void xf86InitValuatorDefaults(DeviceIntPtr dev, int axnum) { + /*if(dev->valuator){ + xf86Msg(X_CONFIG, "registering in xf86InitValuatorDefaults"); + dev->valuator->AccelInitProc = ApplyExtendedValuatorSettings; + }*/ if (axnum == 0) { dev->valuator->axisVal[0] = screenInfo.screens[0]->width / 2; dev->valuator->lastx = dev->valuator->axisVal[0]; @@ -833,6 +974,10 @@ xf86DisableDevice(DeviceIntPtr dev, Bool panic) _X_EXPORT void xf86EnableDevice(DeviceIntPtr dev) { + /*if(dev->valuator){ + xf86Msg(X_CONFIG, "registering in xf86EnableDevice"); + dev->valuator->AccelInitProc = ApplyExtendedValuatorSettings; + }*/ EnableDevice(dev); } diff --git a/include/input.h b/include/input.h index b399d3a..20b3dc4 100644 --- a/include/input.h +++ b/include/input.h @@ -105,6 +105,25 @@ typedef void (*DeviceUnwrapProc)( void* /*data*/ ); +/* pointer acceleration handling */ +typedef void (*PointerAccelSchemeProcPtr)( + DeviceIntPtr /*pDev*/, + int /*first_valuator*/, + int /*num_valuators*/, + int* /*valuators*/, + int /*evtime*/); + +/* callback to initialize scheme (when valuators exist, not used) */ +/*typedef void (*InitSchemeProcPtr)( + DeviceIntPtr pDev);*/ + +/* pointer to cleanup scheme + * TODO check if something like a GeneralDeviceCallbackPtr exists + */ +typedef void (*CleanupAccelSchemeProcPtr)( + DeviceIntPtr /*pDev*/); + + typedef struct _DeviceRec { pointer devicePrivate; ProcessInputProc processInputProc; /* current */ @@ -240,6 +259,15 @@ extern Bool InitValuatorClassDeviceStruct( int /*numMotionEvents*/, int /*mode*/); +extern Bool InitPointerAccelerationScheme( + DeviceIntPtr /*dev*/, + PointerAccelSchemeProcPtr /*schemeProc*/, + void* /*schemeData*/, + CleanupAccelSchemeProcPtr /*cleanupProc*/); + +extern Bool InitDefaultPointerAccelerationScheme( + DeviceIntPtr /*dev*/); + extern Bool InitAbsoluteClassDeviceStruct( DeviceIntPtr /*device*/); diff --git a/include/inputstr.h b/include/inputstr.h index d0cc858..5d1bcde 100644 --- a/include/inputstr.h +++ b/include/inputstr.h @@ -143,7 +143,16 @@ typedef struct _AxisInfo { int max_resolution; int min_value; int max_value; -} AxisInfo, *AxisInfoPtr; + } AxisInfo, *AxisInfoPtr; + + +typedef struct _ValuatorAccelerationRec { + PointerAccelSchemeProcPtr AccelSchemeProc; + void *accelData; /* at disposal of AccelScheme */ + CleanupAccelSchemeProcPtr AccelCleanupProc; + /*InitSchemeProcPtr AccelInitProc;*/ + +} ValuatorAccelerationRec, *ValuatorAccelerationPtr; typedef struct _ValuatorClassRec { ValuatorMotionProcPtr GetMotionProc; @@ -159,7 +168,7 @@ typedef struct _ValuatorClassRec { int *axisVal; int lastx, lasty; /* last event recorded, not posted to * client; see dix/devices.c */ - int dxremaind, dyremaind; /* for acceleration */ + float dxremaind, dyremaind; /* for acceleration */ CARD8 mode; } ValuatorClassRec, *ValuatorClassPtr; diff --git a/include/ptrveloc.h b/include/ptrveloc.h new file mode 100644 index 0000000..f8dd95c --- /dev/null +++ b/include/ptrveloc.h @@ -0,0 +1,79 @@ +/* +* 2006/2007 by Simon Thum +*/ + +#ifndef POINTERVELOCITY_H +#define POINTERVELOCITY_H + +#include /*only needed for scheme decls */ +#include +#include + + +struct _DeviceVelocityData; + +/** + * profile + * returns actual acceleration for velocity, acceleration control,... + */ +typedef float (*PointerAccelerationProfileFunc) + (struct _DeviceVelocityData* /*pVel*/, + float /*threshold*/, float /*acc*/); + + +/** + * Contains all data needed to implement mouse ballistics + */ +typedef struct _DeviceVelocityData { + int lrm_time; /* time the last motion event was processed */ + float velocity; /* velocity as guessed by algorithm */ + int last_dx, last_dy; /* last motion delta */ + int last_diff; /* last time-diff */ + float* fading_lut; /* lookup for IIR filter */ + int fading_lut_size; /* size of lookup table */ + float corr_mul; /* config: multiply this into velocity */ + float rdecay; /* config: (reciprocal) decay in ms */ + float const_acceleration; /* config: (reciprocal) constant deceleration */ + float min_acceleration; /* config: minimum acceleration */ + short reset_time; /* config: reset non-visible state after # ms */ + short use_softening; /* config: use softening of mouse values */ + short use_coupling; /* config: use coupling */ + float coupling; /* config: max. divergence before coupling happens*/ + PointerAccelerationProfileFunc Profile; + void* profile_private;/* extended data, currently unused */ +} DeviceVelocityData, *DeviceVelocityPtr; + + +extern void +InitVelocityData(DeviceVelocityPtr s); + +extern void +FreeVelocityData(void* s); + +extern void +InitFadingLookup(DeviceVelocityPtr s, float rdecay, int lutsize); + +extern int +SetAccelerationProfile(DeviceVelocityPtr s, int profile_num); + +/*extern void +AccelerationDefaultInit(DeviceIntPtr pDev);*/ + +extern Bool +HandleRequestGetAccelDevCtrl(DeviceIntPtr pDev, xDeviceAccelerationCtl* ctl); + +extern Bool +HandleRequestChangeAccelDevCtrl(DeviceIntPtr pDev, xDeviceAccelerationCtl* ctl); + +extern void +AccelerationDefaultCleanup(DeviceIntPtr pDev); + +extern void +acceleratePointer(DeviceIntPtr pDev, int first_valuator, + int num_valuators, int *valuators, int evtime); + +extern void +acceleratePointerPlain(DeviceIntPtr pDev, int first_valuator, + int num_valuators, int *valuators, int ignore); + +#endif /* POINTERVELOCITY_H */