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 <tero.saarni@gmail.com>
Signed-off-by: Peter Hutterer <peter.hutterer@who-t.net>
This commit is contained in:
Tero Saarni
2009-07-12 19:24:40 +03:00
committed by Peter Hutterer
parent afb60a0b24
commit 0c3fbceb1b
7 changed files with 99 additions and 5 deletions

View File

@@ -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_ */

View File

@@ -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.

View File

@@ -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));

View File

@@ -28,6 +28,7 @@
#define _EVENTCOMM_H_
#include <linux/input.h>
#include <linux/version.h>
/* for auto-dev: */
#define DEV_INPUT_EVENT "/dev/input"

View File

@@ -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
@@ -609,6 +615,10 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
para->grab_event_device = *(BOOL*)prop->data;
} else if (property == prop_capabilities)
{
/* read-only */
return BadValue;
} else if (property == prop_resolution)
{
/* read-only */
return BadValue;

View File

@@ -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;
}
}

View File

@@ -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 */