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

Takashi Iwai tiwai at suse.de
Fri Oct 8 10:22:31 PDT 2010


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.

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