[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, ¶->touch_button_area);
+ prop_touch_button_sticky = InitAtom(local->dev, SYNAPTICS_PROP_TOUCH_BUTTON_STICKY, 32, 1, ¶->touch_button_sticky);
+
prop_led = InitAtom(local->dev, SYNAPTICS_PROP_LED, 8, 1, ¶->has_led);
prop_led_status = InitAtom(local->dev, SYNAPTICS_PROP_LED_STATUS, 8, 1, ¶->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