From a88042516c07a759f71f9227917f1083a7d800e3 Mon Sep 17 00:00:00 2001 From: Tero Saarni Date: Sun, 12 Jul 2009 19:24:40 +0300 Subject: [PATCH] Add configurable x/y resolution to fix sensitivity on wide touchpads. Synaptics uses anisotropic coordinate system. On some wide touchpads vertical resolution can be twice as high as horizontal which causes unequal sensitivity on x/y directions. VertResolution and HorizResolution can be used to set the resolution. The ratio of the values is used to compensate x/y sensitivity. The properties are configured automatically if touchpad reports resolution and if running on linux 2.6.31 or newer. Fixes xorg bug #18351. Signed-off-by: Tero Saarni --- include/synaptics-properties.h | 3 ++ man/synaptics.man | 25 +++++++++++++++++- src/eventcomm.c | 8 +++++- src/eventcomm.h | 1 + src/properties.c | 10 +++++++ src/synaptics.c | 52 ++++++++++++++++++++++++++++++++++++++- src/synapticsstr.h | 5 ++++ 7 files changed, 99 insertions(+), 5 deletions(-) diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h index b944adb..3153d73 100644 --- a/include/synaptics-properties.h +++ b/include/synaptics-properties.h @@ -149,4 +149,7 @@ * has_double, has_triple */ #define SYNAPTICS_PROP_CAPABILITIES "Synaptics Capabilities" +/* 32 bit unsigned, 2 values, vertical, horizontal in units/millimeter */ +#define SYNAPTICS_PROP_RESOLUTION "Synaptics Pad Resolution" + #endif /* _SYNAPTICS_PROPERTIES_H_ */ diff --git a/man/synaptics.man b/man/synaptics.man index 5b98082..a69c3c4 100644 --- a/man/synaptics.man +++ b/man/synaptics.man @@ -425,6 +425,23 @@ touching again and moving the finger on the touchpad. The gesture is enabled by default and can be disabled by setting the TapAndDragGesture option to false. Property: "Synaptics Gestures" . +.TP +.BI "Option \*qVertResolution\*q \*q" integer \*q +Resolution of X coordinates in units/millimeter. The value is used +together with HorizResolution to compensate unequal vertical and +horizontal sensitivity. Setting VertResolution and HorizResolution +equal values means no compensation. Default value is read from +the touchpad or set to 1 if value could not be read. +Property: "Synaptics Pad Resolution" +. +.TP +.BI "Option \*qHorizResolution\*q \*q" integer \*q +Resolution of Y coordinates in units/millimeter. The value is used +together with VertResolution to compensate unequal vertical and +horizontal sensitivity. Setting VertResolution and HorizResolution +equal values means no compensation. Default value is read from +the touchpad or set to 1 if value could not be read. +Property: "Synaptics Pad Resolution" .LP The LeftEdge, RightEdge, TopEdge and BottomEdge parameters are used to define the edge and corner areas of the touchpad. @@ -812,6 +829,10 @@ scrolling. 8 bit (BOOL), 5 values (read-only), has left button, has middle button, has right button, two-finger detection, three-finger detection. +.TP 7 +.BI "Synaptics Pad Resolution" +32 bit unsigned, 2 values (read-only), vertical, horizontal in units/millimeter. + .SH "NOTES" There is an example hal policy file in .I ${sourcecode}/fdi/11-x11-synaptics.fdi @@ -827,8 +848,8 @@ If either of (default) or .BI "Protocol \*q" event \*q is used, the driver initializes defaults based on the capabilities reported by -the kernel driver. Acceleration and edges are based on the dimensions reported -by the kernel. If the kernel reports multi-finger detection, two-finger +the kernel driver. Acceleration, edges and resolution are based on the dimensions +reported by the kernel. If the kernel reports multi-finger detection, two-finger vertical scrolling is enabled, horizontal two-finger scrolling is disabled and edge scrolling is disabled. If no multi-finger capabilities are reported, edge scrolling is enabled for both horizontal and vertical scrolling. diff --git a/src/eventcomm.c b/src/eventcomm.c index 5ab34c0..ae853f2 100644 --- a/src/eventcomm.c +++ b/src/eventcomm.c @@ -171,7 +171,7 @@ static void event_query_axis_ranges(LocalDevicePtr local) { SynapticsPrivate *priv = (SynapticsPrivate *)local->private; - struct input_absinfo abs; + struct input_absinfo abs = {0}; unsigned long absbits[NBITS(ABS_MAX)] = {0}; unsigned long keybits[NBITS(KEY_MAX)] = {0}; char buf[256]; @@ -184,6 +184,9 @@ event_query_axis_ranges(LocalDevicePtr local) abs.minimum, abs.maximum); priv->minx = abs.minimum; priv->maxx = abs.maximum; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resx = abs.resolution; +#endif } else xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", local->name, strerror(errno)); @@ -195,6 +198,9 @@ event_query_axis_ranges(LocalDevicePtr local) abs.minimum, abs.maximum); priv->miny = abs.minimum; priv->maxy = abs.maximum; +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,6,30) + priv->resy = abs.resolution; +#endif } else xf86Msg(X_ERROR, "%s: failed to query axis range (%s)\n", local->name, strerror(errno)); diff --git a/src/eventcomm.h b/src/eventcomm.h index a4a9a44..8fd7bcb 100644 --- a/src/eventcomm.h +++ b/src/eventcomm.h @@ -28,6 +28,7 @@ #define _EVENTCOMM_H_ #include +#include /* for auto-dev: */ #define DEV_INPUT_EVENT "/dev/input" diff --git a/src/properties.c b/src/properties.c index 64dbc9e..43bcabb 100644 --- a/src/properties.c +++ b/src/properties.c @@ -82,6 +82,7 @@ Atom prop_pressuremotion_factor = 0; Atom prop_grab = 0; Atom prop_gestures = 0; Atom prop_capabilities = 0; +Atom prop_resolution = 0; static Atom InitAtom(DeviceIntPtr dev, char *name, int format, int nvalues, int *values) @@ -262,6 +263,11 @@ InitDeviceProperties(LocalDevicePtr local) values[3] = priv->has_double; values[4] = priv->has_triple; prop_capabilities = InitAtom(local->dev, SYNAPTICS_PROP_CAPABILITIES, 8, 5, values); + + values[0] = para->resolution_vert; + values[1] = para->resolution_horiz; + prop_resolution = InitAtom(local->dev, SYNAPTICS_PROP_RESOLUTION, 32, 2, values); + } int @@ -612,6 +618,10 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop, { /* read-only */ return BadValue; + } else if (property == prop_resolution) + { + /* read-only */ + return BadValue; } return Success; diff --git a/src/synaptics.c b/src/synaptics.c index 0a3a21a..d95aa30 100644 --- a/src/synaptics.c +++ b/src/synaptics.c @@ -118,6 +118,8 @@ static Bool DeviceOff(DeviceIntPtr); static Bool DeviceClose(DeviceIntPtr); static Bool QueryHardware(LocalDevicePtr); static void ReadDevDimensions(LocalDevicePtr); +static void ScaleCoordinates(SynapticsPrivate *priv, struct SynapticsHwState *hw); +static void CalculateScalingCoeffs(SynapticsPrivate *priv); #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 3 void InitDeviceProperties(LocalDevicePtr local); @@ -338,6 +340,8 @@ static void set_default_parameters(LocalDevicePtr local) int clickFinger1, clickFinger2, clickFinger3; Bool vertEdgeScroll, horizEdgeScroll; Bool vertTwoFingerScroll, horizTwoFingerScroll; + int horizResolution = 1; + int vertResolution = 1; /* read the parameters */ if (priv->synshm) @@ -439,6 +443,12 @@ static void set_default_parameters(LocalDevicePtr local) vertTwoFingerScroll = priv->has_double ? TRUE : FALSE; horizTwoFingerScroll = FALSE; + /* Use resolution reported by hardware if available */ + if ((priv->resx > 0) && (priv->resy > 0)) { + horizResolution = priv->resx; + vertResolution = priv->resy; + } + /* set the parameters */ priv->edges_forced = 0; if (xf86CheckIfOptionUsedByName(opts, "LeftEdge")) @@ -517,6 +527,8 @@ static void set_default_parameters(LocalDevicePtr local) pars->press_motion_max_factor = xf86SetRealOption(opts, "PressureMotionMaxFactor", 1.0); pars->grab_event_device = xf86SetBoolOption(opts, "GrabEventDevice", TRUE); pars->tap_and_drag_gesture = xf86SetBoolOption(opts, "TapAndDragGesture", TRUE); + pars->resolution_horiz = xf86SetIntOption(opts, "HorizResolution", horizResolution); + pars->resolution_vert = xf86SetIntOption(opts, "VertResolution", vertResolution); /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */ if (pars->top_edge > pars->bottom_edge) { @@ -619,6 +631,8 @@ SynapticsPreInit(InputDriverPtr drv, IDevPtr dev, int flags) set_default_parameters(local); + CalculateScalingCoeffs(priv); + if (!alloc_param_data(local)) goto SetupProc_fail; @@ -905,7 +919,7 @@ DeviceInit(DeviceIntPtr dev) #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 axes_labels[0], #endif - min, max, 1, 0, 1); + min, max, priv->resx * 1000, 0, priv->resx * 1000); xf86InitValuatorDefaults(dev, 0); /* Y valuator */ @@ -923,7 +937,7 @@ DeviceInit(DeviceIntPtr dev) #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 7 axes_labels[1], #endif - min, max, 1, 0, 1); + min, max, priv->resy * 1000, 0, priv->resy * 1000); xf86InitValuatorDefaults(dev, 1); #if GET_ABI_MAJOR(ABI_XINPUT_VERSION) == 0 @@ -2229,6 +2243,13 @@ HandleState(LocalDevicePtr local, struct SynapticsHwState *hw) if (timeleft > 0) delay = MIN(delay, timeleft); + /* + * Compensate for unequal x/y resolution. This needs to be done after + * calculations that require unadjusted coordinates, for example edge + * detection. + */ + ScaleCoordinates(priv, hw); + timeleft = ComputeDeltas(priv, hw, edge, &dx, &dy); delay = MIN(delay, timeleft); @@ -2414,3 +2435,30 @@ QueryHardware(LocalDevicePtr local) return TRUE; } +static void +ScaleCoordinates(SynapticsPrivate *priv, struct SynapticsHwState *hw) +{ + int xCenter = (priv->synpara.left_edge + priv->synpara.right_edge) / 2; + int yCenter = (priv->synpara.top_edge + priv->synpara.bottom_edge) / 2; + + hw->x = (hw->x - xCenter) * priv->horiz_coeff + xCenter; + hw->y = (hw->y - yCenter) * priv->vert_coeff + yCenter; +} + +void +CalculateScalingCoeffs(SynapticsPrivate *priv) +{ + int vertRes = priv->synpara.resolution_vert; + int horizRes = priv->synpara.resolution_horiz; + + if ((horizRes > vertRes) && (horizRes > 0)) { + priv->horiz_coeff = vertRes / (double)horizRes; + priv->vert_coeff = 1; + } else if ((horizRes < vertRes) && (vertRes > 0)) { + priv->horiz_coeff = 1; + priv->vert_coeff = horizRes / (double)vertRes; + } else { + priv->horiz_coeff = 1; + priv->vert_coeff = 1; + } +} diff --git a/src/synapticsstr.h b/src/synapticsstr.h index 87af600..8348e72 100644 --- a/src/synapticsstr.h +++ b/src/synapticsstr.h @@ -164,6 +164,8 @@ typedef struct _SynapticsParameters double press_motion_max_factor; /* factor applied on speed when finger pressure is at minimum */ Bool grab_event_device; /* grab event device for exclusive use? */ Bool tap_and_drag_gesture; /* Switches the tap-and-drag gesture on/off */ + unsigned int resolution_horiz; /* horizontal resolution of touchpad in units/mm */ + unsigned int resolution_vert; /* vertical resolution of touchpad in units/mm */ } SynapticsParameters; @@ -224,9 +226,12 @@ typedef struct _SynapticsPrivateRec palm/finger contact disappears */ int prev_z; /* previous z value, for palm detection */ int avg_width; /* weighted average of previous fingerWidth values */ + double horiz_coeff; /* normalization factor for x coordintes */ + double vert_coeff; /* normalization factor for y coordintes */ int minx, maxx, miny, maxy; /* min/max dimensions as detected */ int minp, maxp, minw, maxw; /* min/max pressure and finger width as detected */ + int resx, resy; /* resolution of coordinates as detected in units/mm */ Bool has_left; /* left button detected for this device */ Bool has_right; /* right button detected for this device */ Bool has_middle; /* middle button detected for this device */ -- 1.6.0.4