[PATCH 07/18] Allow touching in clickpad button area

Peter Hutterer peter.hutterer at who-t.net
Tue Oct 12 21:26:25 PDT 2010


On Fri, Oct 08, 2010 at 07:22:31PM +0200, Takashi Iwai wrote:
> Instead of ignoring the mouse touch sense in the clickpad button area,
> introduce a "stickiness" to the movement for blocking the unexpected
> movement of the driver at clicking.

there are two changes in this patch - one is the configurable bottom area,
one is the stickiness. please split them up into two patches, the first one
should go into the general clickpad patch then.

For both properties - if the touchpad is not a clickpad the properties
should not be initialised on the device.

Cheers,
  Peter

> Signed-off-by: Takashi Iwai <tiwai at suse.de>
> ---
>  include/synaptics-properties.h |    6 ++
>  man/synaptics.man              |   30 +++++++-
>  src/properties.c               |   15 ++++
>  src/synaptics.c                |  156 +++++++++++++++++++++++++++++-----------
>  src/synapticsstr.h             |    5 ++
>  tools/synclient.c              |    2 +
>  6 files changed, 169 insertions(+), 45 deletions(-)
> 
> diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
> index 1343e6d..47f6bb1 100644
> --- a/include/synaptics-properties.h
> +++ b/include/synaptics-properties.h
> @@ -155,6 +155,12 @@
>  /* 32 bit, 4 values, left, right, top, bottom */
>  #define SYNAPTICS_PROP_AREA "Synaptics Area"
>  
> +/* 32bit */
> +#define SYNAPTICS_PROP_TOUCH_BUTTON_AREA "Synaptics Touch Button Area"
> +
> +/* 32bit */
> +#define SYNAPTICS_PROP_TOUCH_BUTTON_STICKY "Synapyics Touch Button Sticky"
> +
>  /* 8 bit (BOOL, read-only), has_led */
>  #define SYNAPTICS_PROP_LED "Synaptics LED"
>  
> diff --git a/man/synaptics.man b/man/synaptics.man
> index d8afa1a..6d4a886 100644
> --- a/man/synaptics.man
> +++ b/man/synaptics.man
> @@ -554,6 +554,21 @@ A "touch" event happens when the Z value goes above FingerHigh, and an
>  "untouch" event happens when the Z value goes below FingerLow.
>  .
>  .TP
> +.BI "Option \*qTouchButtonArea\*q \*q" integer \*q
> +.
> +Provides the size of the click-button area for ClickPad devices in
> +percent. As default, it's 20.
> +Property: "Synaptics Touch Button Area"
> +.
> +.TP
> +.BI "Option \*qTouchButtonSticky\*q \*q" integer \*q
> +.
> +Provides the threshold to start moving in the click-button area.  The
> +higher value is set, the pointer becomes sticky in the click-button
> +area. The default value is 64.
> +Property: "Synaptics Touch Button Sticky"
> +.
> +.TP
>  .BI "Option \*qLEDDoubleTap\*q \*q" boolean \*q
>  .
>  Enables/disables the touchpad-control by double-tapping on the top-left
> @@ -744,10 +759,9 @@ When the input device reports a device with a single left button
>  and without multi-fingers, the driver assumes it's a Synaptics
>  Clickpad device and handles it in ClickZone mode.  In this mode,
>  a left/right/middle button event is generated according to the
> -position you click.  When Clickpad is enabled, the touchpad area
> -is shrunk as default in 20% and the bottom area is used as the
> -click-button area.  The area can be configurable via options or
> -properties below.
> +position you click.  When Clickpad is enabled, the bottom area (as
> +default 20%) is used as the click-button area.  The size of the area
> +is configurable via options or properties below.
>  
>  .SH "DEVICE PROPERTIES"
>  Synaptics 1.0 and higher support input device properties if the driver is
> @@ -923,6 +937,14 @@ right button, two-finger detection, three-finger detection, pressure detection,
>  32 bit unsigned, 2 values (read-only), vertical, horizontal in units/millimeter.
>  
>  .TP 7
> +.BI "Synaptics Touch Button Area"
> +32 bit.
> +
> +.TP 7
> +.BI "Synaptics Touch Button Sticky"
> +32 bit.
> +
> +.TP 7
>  .BI "Synaptics LED"
>  8 bit (BOOL, read-only), indicating whether the device has an embedded
>  LED support or not.
> diff --git a/src/properties.c b/src/properties.c
> index 6862a09..4042edc 100644
> --- a/src/properties.c
> +++ b/src/properties.c
> @@ -82,6 +82,8 @@ Atom prop_gestures              = 0;
>  Atom prop_capabilities          = 0;
>  Atom prop_resolution            = 0;
>  Atom prop_area                  = 0;
> +Atom prop_touch_button_area     = 0;
> +Atom prop_touch_button_sticky   = 0;
>  Atom prop_led                   = 0;
>  Atom prop_led_status            = 0;
>  Atom prop_led_double_tap        = 0;
> @@ -282,6 +284,9 @@ InitDeviceProperties(InputInfoPtr pInfo)
>      values[3] = para->area_bottom_edge;
>      prop_area = InitAtom(pInfo->dev, SYNAPTICS_PROP_AREA, 32, 4, values);
>  
> +    prop_touch_button_area = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_AREA, 32, 1, &para->touch_button_area);
> +    prop_touch_button_sticky = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_STICKY, 32, 1, &para->touch_button_sticky);
> +
>      prop_led = InitAtom(local->dev, SYNAPTICS_PROP_LED, 8, 1, &para->has_led);
>      prop_led_status = InitAtom(local->dev, SYNAPTICS_PROP_LED_STATUS, 8, 1, &para->led_status);
>  
> @@ -671,6 +676,16 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
>          para->area_right_edge  = area[1];
>          para->area_top_edge    = area[2];
>          para->area_bottom_edge = area[3];
> +    } else if (property == prop_touch_button_area) {
> +        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
> +            return BadMatch;
> +
> +	para->touch_button_area = *(INT32*)prop->data;
> +    } else if (property == prop_touch_button_sticky) {
> +        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
> +            return BadMatch;
> +
> +	para->touch_button_sticky = *(INT32*)prop->data;
>      } else if (property == prop_led_status)
>      {
>          if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
> diff --git a/src/synaptics.c b/src/synaptics.c
> index 26ef666..3ba918a 100644
> --- a/src/synaptics.c
> +++ b/src/synaptics.c
> @@ -516,18 +516,6 @@ static void set_default_parameters(InputInfoPtr pInfo)
>          vertResolution = priv->resy;
>      }
>  
> -    /* Clickpad mode -- bottom area is used as buttons */
> -    if (priv->is_clickpad) {
> -	int button_bottom;
> -	/* Clickpad devices usually the button area at the bottom, and
> -	 * its size seems ca. 20% of the touchpad height no matter how
> -	 * large the pad is.
> -	 */
> -	button_bottom = priv->maxy - (abs(priv->maxy - priv->miny) * 20) / 100;
> -	if (button_bottom < b && button_bottom >= t)
> -	    b = button_bottom;
> -    }
> -
>      /* set the parameters */
>      pars->left_edge = xf86SetIntOption(opts, "LeftEdge", l);
>      pars->right_edge = xf86SetIntOption(opts, "RightEdge", r);
> @@ -536,10 +524,6 @@ static void set_default_parameters(InputInfoPtr pInfo)
>  
>      pars->area_top_edge = set_percent_option(opts, "AreaTopEdge", height, priv->miny);
>      pars->area_bottom_edge = set_percent_option(opts, "AreaBottomEdge", height, priv->miny);
> -    /* in clickpad mode, we don't want to sense the button area as default */
> -    if (pars->area_bottom_edge == 0 && priv->is_clickpad)
> -	pars->area_bottom_edge = b;
> -
>      pars->area_left_edge = set_percent_option(opts, "AreaLeftEdge", width, priv->minx);
>      pars->area_right_edge = set_percent_option(opts, "AreaRightEdge", width, priv->minx);
>  
> @@ -609,6 +593,8 @@ static void set_default_parameters(InputInfoPtr pInfo)
>      pars->tap_and_drag_gesture = xf86SetBoolOption(opts, "TapAndDragGesture", TRUE);
>      pars->resolution_horiz = xf86SetIntOption(opts, "HorizResolution", horizResolution);
>      pars->resolution_vert = xf86SetIntOption(opts, "VertResolution", vertResolution);
> +    pars->touch_button_area = xf86SetIntOption(opts, "TouchButtonArea", 20);

as of server 1.9, we have xf86SetPercentOption to parse values of Option
"foo" "20%". Please use that as well.


> +    pars->touch_button_sticky = xf86SetIntOption(opts, "TouchButtonSticky", 64);
>      pars->led_double_tap = xf86SetBoolOption(opts, "LEDDoubleTap", TRUE);
>  
>      /* Warn about (and fix) incorrectly configured TopEdge/BottomEdge parameters */
> @@ -1107,6 +1093,11 @@ DeviceInit(DeviceIntPtr dev)
>      return Success;
>  }
>  
> +static void
> +SetTapState(SynapticsPrivate *priv, enum TapState tap_state, int millis);
> +static void
> +SetMovingState(SynapticsPrivate *priv, enum MovingState moving_state, int millis);
> +
>  #define LED_TOGGLE_X_AREA	0.10
>  #define LED_TOGGLE_Y_AREA	0.08
>  
> @@ -1173,45 +1164,118 @@ handle_toggle_led(LocalDevicePtr local, struct SynapticsHwState *hw, int finger)
>      return 0; /* already processed; ignore this finger event */
>  }
>  
> +static void
> +get_clickpad_button(SynapticsPrivate *priv, struct SynapticsHwState *hw,
> +		    int button_x)
> +{
> +    int width = priv->maxx - priv->minx;
> +    int left_button_x, right_button_x;
> +
> +    /* left and right clickpad button ranges;
> +     * the gap between them is interpreted as a middle-button click
> +     */
> +    left_button_x = width * 2 / 5 + priv->minx;
> +    right_button_x = width * 3 / 5 + priv->minx;
> +
> +    /* clickpad reports only one button, and we need
> +     * to fake left/right buttons depending on the touch position
> +     */
> +    if (button_x < left_button_x)
> +	hw->left = 1;
> +    else if (button_x > right_button_x)
> +	hw->right = 1;
> +    else
> +	hw->middle = 1;
> +}
> +
> +static inline int get_touch_button_area(SynapticsPrivate *priv)
> +{
> +    SynapticsParameters *para = &priv->synpara;
> +    return priv->maxy - (priv->maxy - priv->miny) * para->touch_button_area / 100;
> +}
> +
> +#define is_main_bottom_edge(hw, priv) \
> +    ((hw)->y >= get_touch_button_area(priv))

do we really need a macro for this?
it's only used once and quite understandable if written normally.

> +
> +static void reset_state_as_moving(SynapticsPrivate *priv, struct SynapticsHwState *hw)
> +{
> +    SynapticsParameters *para = &priv->synpara;
> +
> +    if (hw->z >= para->finger_low) {
> +	SetMovingState(priv, MS_TOUCHPAD_RELATIVE, hw->millis);
> +	SetTapState(priv, TS_MOVE, hw->millis);
> +	priv->touch_on.x = hw->x;
> +	priv->touch_on.y = hw->y;
> +	priv->touch_on.millis = hw->millis;
> +    }
> +    priv->tap_button = 0;
> +}
> +
>  /* clickpad event handling */
>  static void
>  handle_clickpad(LocalDevicePtr local, struct SynapticsHwState *hw)
>  {
>      SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
>      SynapticsParameters *para = &priv->synpara;
> +    int in_main_button;
> +
> +    in_main_button = is_main_bottom_edge(hw, priv);
> +    if (in_main_button) {
> +	if (hw->left) {
> +	    /* when button is pressed solely, don't move and ignore tapping */
> +	    hw->z = 0;
> +	    priv->count_packet_finger = 0;
> +	    priv->ignore_tapping = TRUE;
> +	    SetMovingState(priv, MS_FALSE, hw->millis);
> +	    SetTapState(priv, TS_START, hw->millis);
> +	    priv->tap_button = 0;
> +	} else if (hw->z < para->finger_low) {
> +	    priv->ignore_tapping = FALSE;
> +	}
> +    } else {
> +	priv->ignore_tapping = FALSE;
> +    }
>  
>      if (hw->left) { /* clicked? */
> -	if (hw->y > para->bottom_edge) {
> -	    /* button area */
> -	    int width = priv->maxx - priv->minx;
> -	    int left_button_x, right_button_x;
> -
> -	    /* left and right clickpad button ranges;
> -	     * the gap between them is interpreted as a middle-button click
> -	     */
> -	    left_button_x = width * 2 / 5 + priv->minx;
> -	    right_button_x = width * 3 / 5 + priv->minx;
> -
> -	    /* clickpad reports only one button, and we need
> -	     * to fake left/right buttons depending on the touch position
> -	     */
> -	    hw->left = 0;
> -	    if (hw->x < left_button_x)
> -		hw->left = 1;
> -	    else if (hw->x > right_button_x)
> -		hw->right = 1;
> -	    else
> -		hw->middle = 1;
> -	} else {
> -	    /* dragging */
> +	if (priv->prev_hw.left || priv->prev_hw.right || priv->prev_hw.middle) {
> +	    /* already dragging, just copy the previous button state */
>  	    hw->left = priv->prev_hw.left;
>  	    hw->right = priv->prev_hw.right;
>  	    hw->middle = priv->prev_hw.middle;
> +	} else if (in_main_button) {
> +	    /* start dragging */
> +	    hw->left = 0;
> +	    if (in_main_button)
> +		get_clickpad_button(priv, hw, hw->x);
> +	}
> +    } else {
> +	/* button being released, reset dragging if necessary */
> +	if (priv->prev_hw.left || priv->prev_hw.right || priv->prev_hw.middle) {
> +	    priv->count_packet_finger = 0;
> +	    reset_state_as_moving(priv, hw);
>  	}
> +	hw->left = hw->right = hw->middle = 0;
> +    }
> +
> +    if (in_main_button && para->touch_button_sticky > 0) {
> +	if (!priv->count_packet_finger)  {
> +	    /* if the primary track point is in the button area, be sticky */
> +	    priv->clickpad_threshold = para->touch_button_sticky;
> +	    priv->clickpad_dx = priv->clickpad_dy = 0;
> +	}
> +    } else {
> +	/* outside the button area, clear stickiness */
> +	priv->clickpad_threshold = 0;
>      }
>      priv->prev_hw = *hw;
>  }
>  
> +static int
> +move_distance(int dx, int dy)
> +{
> +    return sqrt(SQR(dx) + SQR(dy));
> +}
> +
>  /*
>   * Convert from absolute X/Y coordinates to a coordinate system where
>   * -1 corresponds to the left/upper edge and +1 corresponds to the
> @@ -1688,7 +1752,7 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
>      int timeleft, timeout;
>      int delay = 1000000000;
>  
> -    if (priv->palm)
> +    if (priv->palm || priv->ignore_tapping)
>  	return delay;
>  
>      touch = finger && !priv->finger_state;
> @@ -2393,6 +2457,8 @@ update_hw_button_state(const InputInfoPtr pInfo, struct SynapticsHwState *hw, in
>      if (para->touchpad_off != 1 && priv->is_clickpad)
>  	handle_clickpad(local, hw);
>  
> +    priv->prev_hw = *hw;
> +
>      /* Treat the first two multi buttons as up/down for now. */
>      hw->up |= hw->multi[0];
>      hw->down |= hw->multi[1];
> @@ -2575,6 +2641,14 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
>      }
>  
>      timeleft = ComputeDeltas(priv, hw, edge, &dx, &dy, inside_active_area);
> +    if (priv->clickpad_threshold > 0) {
> +	priv->clickpad_dx += dx;
> +	priv->clickpad_dy += dy;
> +	if (move_distance(priv->clickpad_dx, priv->clickpad_dy) > priv->clickpad_threshold)
> +	    priv->clickpad_threshold = 0;
> +	else
> +	    dx = dy = 0;
> +    }
>      delay = MIN(delay, timeleft);
>  
>  
> @@ -2586,7 +2660,7 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
>  	       (hw->multi[2] ? 0x20 : 0) |
>  	       (hw->multi[3] ? 0x40 : 0));
>  
> -    if (priv->tap_button > 0) {
> +    if (priv->tap_button > 0 && !priv->ignore_tapping) {
>  	int tap_mask = 1 << (priv->tap_button - 1);
>  	if (priv->tap_button_state == TBS_BUTTON_DOWN_UP) {
>  	    if (tap_mask != (priv->lastButtons & tap_mask)) {
> diff --git a/src/synapticsstr.h b/src/synapticsstr.h
> index 05308c1..44140f2 100644
> --- a/src/synapticsstr.h
> +++ b/src/synapticsstr.h
> @@ -160,6 +160,8 @@ typedef struct _SynapticsParameters
>      unsigned int resolution_horiz;          /* horizontal resolution of touchpad in units/mm */
>      unsigned int resolution_vert;           /* vertical resolution of touchpad in units/mm */
>      int area_left_edge, area_right_edge, area_top_edge, area_bottom_edge; /* area coordinates absolute */
> +    int touch_button_area;                  /* clickpad button area */
> +    int touch_button_sticky;                /* pointer stickiness in button area */
>      Bool has_led;                           /* has an embedded LED */
>      Bool led_status;                        /* Current status of LED (1=on) */
>      Bool led_double_tap;		    /* double-tap period in ms for touchpad LED control */
> @@ -239,6 +241,9 @@ typedef struct _SynapticsPrivateRec
>      Bool has_width;			/* device reports finger width */
>      Bool has_scrollbuttons;		/* device has physical scrollbuttons */
>      Bool is_clickpad;			/* is Clickpad device (one-button) */
> +    Bool ignore_tapping;
> +    unsigned int clickpad_threshold;
> +    int clickpad_dx, clickpad_dy;
>      struct SynapticsHwState prev_hw;	/* previous h/w state (for clickpad) */
>      int prop_change_pending;
>      Bool led_touch_state;
> diff --git a/tools/synclient.c b/tools/synclient.c
> index f7d93e0..b51fe42 100644
> --- a/tools/synclient.c
> +++ b/tools/synclient.c
> @@ -143,6 +143,8 @@ static struct Parameter params[] = {
>      {"AreaRightEdge",         PT_INT,    0, 10000, SYNAPTICS_PROP_AREA,	32,	1},
>      {"AreaTopEdge",           PT_INT,    0, 10000, SYNAPTICS_PROP_AREA,	32,	2},
>      {"AreaBottomEdge",        PT_INT,    0, 10000, SYNAPTICS_PROP_AREA,	32,	3},
> +    {"TouchButtonArea",       PT_INT,    0, 100,   SYNAPTICS_PROP_TOUCH_BUTTON_AREA,	32,	0},
> +    {"TouchButtonSticky",     PT_INT,    0, 1000,  SYNAPTICS_PROP_TOUCH_BUTTON_STICKY,	32,	0},
>      {"LEDStatus",             PT_BOOL,   0, 1,     SYNAPTICS_PROP_LED_STATUS,	8,	0},
>      {"LEDDoubleTap",          PT_BOOL,   0, 1,     SYNAPTICS_PROP_LED_DOUBLE_TAP,	8,	0},
>      { NULL, 0, 0, 0, 0 }
> -- 
> 1.7.3.1
> 


More information about the xorg-devel mailing list