---
 include/synaptics-properties.h |   12 +--
 include/synaptics.h            |    2 
 man/synaptics.man              |   30 ++++++++
 src/properties.c               |   17 ++--
 src/synaptics.c                |  146 +++++++++++++++++++++++++++++------------
 src/synapticsstr.h             |    3 
 tools/synclient.c              |    6 -
 7 files changed, 156 insertions(+), 60 deletions(-)

--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -142,14 +142,14 @@
 /* 8 bit (BOOL) */
 #define SYNAPTICS_PROP_GRAB "Synaptics Grab Event Device"
 
-/* 32 bit */
-#define SYNAPTICS_PROP_LED_DOUBLE_TAP "Synaptics LED Double Tap"
-
-/* 8 bit (0-100) */
+/* 32bit */
 #define SYNAPTICS_PROP_TOUCH_BUTTON_AREA "Synaptics Touch Button Area"
 
-/* 8 bit (BOOL) */
-#define SYNAPTICS_PROP_TOUCH_BUTTON_SENSE "Synaptics Touch Button Sense"
+/* 32bit */
+#define SYNAPTICS_PROP_TOUCH_BUTTON_STICKY "Synapyics Touch Button Sticky"
+
+/* 32 bit */
+#define SYNAPTICS_PROP_LED_DOUBLE_TAP "Synaptics LED Double Tap"
 
 /* FLOAT */
 #define SYNAPTICS_PROP_MAX_SCREEN_SCALE "Synaptics Max Screen Scale"
--- a/include/synaptics.h
+++ b/include/synaptics.h
@@ -136,7 +136,7 @@
     Bool grab_event_device;		    /* grab event device for exclusive use? */
     int led_double_tap;			    /* double-tap period in ms for touchpad LED control */
     int touch_button_area;                  /* touch button (clickpad) area in percent */
-    Bool touch_button_sense;                /* touch button (clickpad) area sensing */
+    int touch_button_sticky;                /* pointer stickiness in button area */
     double max_screen_scale;                /* max screen scaling factor */
 } SynapticsSHM;
 
--- a/man/synaptics.man
+++ b/man/synaptics.man
@@ -411,6 +411,20 @@
 .
 This can be achieved by switching to a text console and then switching
 back to X.
+.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"
 .
 .
 .LP
@@ -635,6 +649,14 @@
 Trackstick mode is exited when the finger pressure drops below
 FingerLow or when the finger is moved further than MaxTapMove away
 from the initial position.
+.
+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 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
@@ -787,6 +809,14 @@
 .BI "Synaptics Grab Event Device"
 8 bit (BOOL).
 
+.TP 7
+.BI "Synaptics Touch Button Area"
+32 bit.
+
+.TP 7
+.BI "Synaptics Touch Button Sticky"
+32 bit.
+
 .SH "NOTES"
 There is an example hal policy file in
 .I ${sourcecode}/fdi/11-x11-synaptics.fdi
--- a/src/properties.c
+++ b/src/properties.c
@@ -82,7 +82,7 @@
 Atom prop_grab                  = 0;
 Atom prop_led_double_tap        = 0;
 Atom prop_touch_button_area     = 0;
-Atom prop_touch_button_sense    = 0;
+Atom prop_touch_button_sticky   = 0;
 Atom prop_max_screen_scale      = 0;
 
 static Atom
@@ -257,9 +257,8 @@
 
     prop_led_double_tap = InitAtom(local->dev, SYNAPTICS_PROP_LED_DOUBLE_TAP, 32, 1, &para->led_double_tap);
 
-    prop_touch_button_area = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_AREA, 8, 1, &para->touch_button_area);
-
-    prop_touch_button_sense = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_SENSE, 8, 1, &para->touch_button_sense);
+    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);
 
     fvalues[0] = para->max_screen_scale;
     prop_max_screen_scale = InitFloatAtom(local->dev, SYNAPTICS_PROP_MAX_SCREEN_SCALE, 1, fvalues);
@@ -622,16 +621,16 @@
         para->led_double_tap = *(INT32*)prop->data;
     } else if (property == prop_touch_button_area)
     {
-        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
             return BadMatch;
 
-        para->touch_button_area = *(CARD8*)prop->data;
-    } else if (property == prop_touch_button_sense)
+	para->touch_button_area = *(INT32*)prop->data;
+    } else if (property == prop_touch_button_sticky)
     {
-        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+        if (prop->size != 1 || prop->format != 32 || prop->type != XA_INTEGER)
             return BadMatch;
 
-        para->touch_button_sense = *(BOOL*)prop->data;
+	para->touch_button_sticky = *(INT32*)prop->data;
     }
 
     return Success;
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -515,7 +515,7 @@
     pars->grab_event_device = xf86SetBoolOption(opts, "GrabEventDevice", TRUE);
     pars->led_double_tap = xf86SetIntOption(opts, "LEDDoubleTap", 400);
     pars->touch_button_area = xf86SetIntOption(opts, "TouchButtonArea", 20);
-    pars->touch_button_sense = xf86SetBoolOption(opts, "TouchButtonSense", FALSE);
+    pars->touch_button_sticky = xf86SetIntOption(opts, "TouchButtonSticky", 64);
 
     pars->max_screen_scale = xf86SetRealOption(opts, "MaxScreenScale", 0.35);
 
@@ -1349,7 +1349,7 @@
     int timeleft, timeout;
     int delay = 1000000000;
 
-    if (priv->palm)
+    if (priv->palm || priv->ignore_tapping)
 	return delay;
 
     touch = finger && !priv->finger_state;
@@ -2073,55 +2073,109 @@
     return 0; /* already processed; ignore this finger event */
 }
 
-/* clickpad event handling */
 static void
-HandleClickpad(LocalDevicePtr local, struct SynapticsHwState *hw)
+get_clickpad_button(SynapticsPrivate *priv, struct SynapticsHwState *hw,
+		    int button_x)
 {
-    SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
-    SynapticsSHM *para = priv->synpara;
     int width = priv->maxx - priv->minx;
-    int height = priv->maxy - priv->miny;
-    int button_area_y, x_vscroll;
     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;
+    left_button_x = width * 2 / 5 + priv->minx;
     right_button_x = width * 3 / 5 + priv->minx;
-    /* button area */
-    button_area_y = priv->maxy - height * para->touch_button_area / 100;
-    /* allow position change for the v-scroll area exceptionally */
-    x_vscroll = priv->maxx - width / 15;
-
-    if (hw->y >= button_area_y) {
-	/* button area */
-	if (!para->touch_button_sense) {
-	    if (hw->left ||
-		/* in h- or v-scrolling area, allow sensing */
-		(!para->scroll_edge_horiz &&
-		 (hw->x < x_vscroll || !para->scroll_edge_vert)))
-	    hw->z = 0; /* don't move pointer */
-	}
-	/* clickpad reports only one button, and we need
-	 * to fake left/right buttons depending on the touch position
-	 */
-	if (hw->left) { /* clicked? */
+
+    /* 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)
+{
+    SynapticsSHM *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)
+{
+    SynapticsSHM *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
+HandleClickpad(LocalDevicePtr local, struct SynapticsHwState *hw)
+{
+    SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
+    SynapticsSHM *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 (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 (hw->x < left_button_x)
-		hw->left = 1;
-	    else if (hw->x > right_button_x)
-		hw->right = 1;
-	    else
-		hw->middle = 1;
-	}
-    } else if (hw->left) {
-	/* dragging */
-	hw->left = priv->prev_hw.left;
-	hw->right = priv->prev_hw.right;
-	hw->middle = priv->prev_hw.middle;
+	    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;
 }
 
 /*
@@ -2173,6 +2227,8 @@
 	para->touch_button_area > 0)
 	HandleClickpad(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];
@@ -2246,6 +2302,14 @@
 
     timeleft = ComputeDeltas(priv, hw, edge, &dx, &dy);
     delay = MIN(delay, timeleft);
+    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;
+    }
 
     rep_buttons = ((para->updown_button_repeat ? 0x18 : 0) |
 		   (para->leftright_button_repeat ? 0x60 : 0));
@@ -2258,7 +2322,7 @@
 	       (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)) {
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -156,6 +156,9 @@
     Bool has_pressure;			/* device reports pressure */
     Bool is_clickpad;			/* is Clickpad device (one-button) */
     Bool has_led;			/* has LED indicator */
+    Bool ignore_tapping;
+    unsigned int clickpad_threshold;
+    int clickpad_dx, clickpad_dy;
     Bool led_touch_state;
     Bool led_tapped;
     int prop_change_pending;
--- a/tools/synclient.c
+++ b/tools/synclient.c
@@ -212,9 +212,9 @@
     DEFINE_PAR("LEDDoubleTap",         led_double_tap,          PT_INT,    0, 1000,
 		SYNAPTICS_PROP_LED_DOUBLE_TAP,	32,	0),
     DEFINE_PAR("TouchButtonArea",      touch_button_area,       PT_INT,    0, 100,
-		SYNAPTICS_PROP_TOUCH_BUTTON_AREA,	8,	0),
-    DEFINE_PAR("TouchButtonSense",     touch_button_sense,      PT_BOOL,   0, 1,
-		SYNAPTICS_PROP_TOUCH_BUTTON_SENSE,	8,	0),
+		SYNAPTICS_PROP_TOUCH_BUTTON_AREA,	32,	0),
+    DEFINE_PAR("TouchButtonSticky",    touch_button_sticky,     PT_INT,    0, 1000,
+		SYNAPTICS_PROP_TOUCH_BUTTON_STICKY,	32,	0),
     DEFINE_PAR("MaxScreenScale",       max_screen_scale,	PT_DOUBLE, 0, 10,
 	       SYNAPTICS_PROP_MAX_SCREEN_SCALE,	0 /* float*/,	0),
     { NULL, 0, 0, 0, 0 }
