summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeter Hutterer <peter.hutterer@who-t.net>2012-03-14 08:48:00 +1000
committerPeter Hutterer <peter.hutterer@who-t.net>2012-03-14 08:48:00 +1000
commit6c457c0c61a0834361f45a073148db7b4c9be40b (patch)
tree90c1b8cfca6e272914439c4fdc22687e38965ed9
parentdefc1d008e5674306a7e9b9cb0c750d9787990b7 (diff)
parentfee18d8567efd2e5abf6b29eb1ae9ee0e3858013 (diff)
Merge branch 'clickpad-v3'
-rw-r--r--include/synaptics-properties.h6
-rw-r--r--man/synaptics.man34
-rw-r--r--src/eventcomm.c76
-rw-r--r--src/properties.c53
-rw-r--r--src/synaptics.c370
-rw-r--r--src/synapticsstr.h7
-rw-r--r--src/synproto.c8
-rw-r--r--src/synproto.h7
-rw-r--r--test/fake-symbols.c5
-rw-r--r--tools/synclient.c10
10 files changed, 528 insertions, 48 deletions
diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
index c550cef..8c20a0c 100644
--- a/include/synaptics-properties.h
+++ b/include/synaptics-properties.h
@@ -49,6 +49,9 @@
#define SYNAPTICS_PROP_TAP_DURATIONS "Synaptics Tap Durations"
/* 8 bit (BOOL) */
+#define SYNAPTICS_PROP_CLICKPAD "Synaptics ClickPad"
+
+/* 8 bit (BOOL) */
#define SYNAPTICS_PROP_TAP_FAST "Synaptics Tap FastTap"
/* 32 bit */
@@ -155,6 +158,9 @@
/* 32 bit, 4 values, left, right, top, bottom */
#define SYNAPTICS_PROP_AREA "Synaptics Area"
+/* 32 bit, 4 values, left, right, top, buttom */
+#define SYNAPTICS_PROP_SOFTBUTTON_AREAS "Synaptics Soft Button Areas"
+
/* 32 Bit Integer, 2 values, horizontal hysteresis, vertical hysteresis */
#define SYNAPTICS_PROP_NOISE_CANCELLATION "Synaptics Noise Cancellation"
diff --git a/man/synaptics.man b/man/synaptics.man
index b6b1dce..23862e3 100644
--- a/man/synaptics.man
+++ b/man/synaptics.man
@@ -143,6 +143,12 @@ Maximum time (in milliseconds) for detecting a double tap. Property:
The duration of the mouse click generated by tapping. Property: "Synaptics Tap
Durations"
.TP 7
+.BI "Option \*qClickPad\*q \*q" boolean \*q
+Whether the device is a click pad. A click pad device has button(s) integrated
+into the touchpad surface. The user must press downward on the touchpad in order
+to generated a button press. This property may be set automatically if a click
+pad device is detected at initialization time. Property: "Synaptics ClickPad"
+.TP 7
.BI "Option \*qFastTaps\*q \*q" boolean \*q
Makes the driver react faster to a single tap, but also makes double
clicks caused by double tapping slower. Property: "Synaptics Tap FastTap"
@@ -512,6 +518,21 @@ AreaBottomEdge option to any integer value other than zero. If supported by the
server (version 1.9 and later), the edge may be specified in percent of
the total height of the touchpad. Property: "Synaptics Area"
.
+.TP
+.BI "Option \*qSoftButtonAreas\*q \*q" "RBL RBR RBT RBB MBL MBR MBT MBB" \*q
+This option is only available on ClickPad devices.
+Enable soft button click area support on ClickPad devices. The first four
+parameters define the area of the right button, and the second four parameters
+define the area of the middle button. The areas are defined by the left, right,
+top, and bottom edges as sequential values of the property. If any edge is set
+to 0, the button is assumed to extend to infinity in the given direction.
+.
+When the user performs a click within the defined soft button areas, the right
+or middle click action is performed.
+.
+The use of soft button areas is disabled by setting all the values for the area
+to 0. Property: "Synaptics Soft Button Areas"
+.
.SH CONFIGURATION DETAILS
.SS Area handling
@@ -785,6 +806,10 @@ Properties supported:
duration of a single click.
.TP 7
+.BI "Synaptics ClickPad"
+8 bit (Bool).
+
+.TP 7
.BI "Synaptics Tap FastTap"
8 bit (BOOL).
@@ -917,6 +942,15 @@ default.
32 bit, 4 values, left, right, top, bottom. 0 disables an element.
.TP 7
+.BI "Synaptics Soft Button Areas"
+This property is only available on ClickPad devices.
+The Right and middle soft button areas are used to support right and middle
+click actions on a ClickPad device. Providing 0 for all values of a given button
+disables the button area.
+
+32 bit, 8 values, RBL, RBR, RBT, RBB, MBL, MBR, MBT, MBB.
+
+.TP 7
.BI "Synaptics Capabilities"
This read-only property expresses the physical capability of the touchpad,
most notably whether the touchpad hardware supports multi-finger tapping and
diff --git a/src/eventcomm.c b/src/eventcomm.c
index 8904851..f199c24 100644
--- a/src/eventcomm.c
+++ b/src/eventcomm.c
@@ -65,10 +65,8 @@ struct eventcomm_proto_data
* exists for readability of the code.
*/
BOOL need_grab;
- int st_to_mt_offset_x;
- double st_to_mt_scale_x;
- int st_to_mt_offset_y;
- double st_to_mt_scale_y;
+ int st_to_mt_offset[2];
+ double st_to_mt_scale[2];
#ifdef HAVE_MULTITOUCH
struct mtdev *mtdev;
int axis_map[MT_ABS_SIZE];
@@ -86,8 +84,8 @@ EventProtoDataAlloc(void)
if (!proto_data)
return NULL;
- proto_data->st_to_mt_scale_x = 1;
- proto_data->st_to_mt_scale_y = 1;
+ proto_data->st_to_mt_scale[0] = 1;
+ proto_data->st_to_mt_scale[1] = 1;
return proto_data;
}
@@ -424,11 +422,11 @@ event_query_axis_ranges(InputInfoPtr pInfo)
event_get_abs(pInfo, pInfo->fd, ABS_MT_POSITION_Y, &priv->miny,
&priv->maxy, &priv->synpara.hyst_y, &priv->resy);
- proto_data->st_to_mt_offset_x = priv->minx - st_minx;
- proto_data->st_to_mt_scale_x =
+ proto_data->st_to_mt_offset[0] = priv->minx - st_minx;
+ proto_data->st_to_mt_scale[0] =
(priv->maxx - priv->minx) / (st_maxx - st_minx);
- proto_data->st_to_mt_offset_y = priv->miny - st_miny;
- proto_data->st_to_mt_scale_y =
+ proto_data->st_to_mt_offset[1] = priv->miny - st_miny;
+ proto_data->st_to_mt_scale[1] =
(priv->maxy - priv->miny) / (st_maxy - st_miny);
}
#endif
@@ -527,6 +525,20 @@ SynapticsReadEvent(InputInfoPtr pInfo, struct input_event *ev)
return rc;
}
+#ifdef HAVE_MULTITOUCH
+static Bool
+EventTouchSlotPreviouslyOpen(SynapticsPrivate *priv, int slot)
+{
+ int i;
+
+ for (i = 0; i < priv->num_active_touches; i++)
+ if (priv->open_slots[i] == slot)
+ return TRUE;
+
+ return FALSE;
+}
+#endif
+
static void
EventProcessTouchEvent(InputInfoPtr pInfo, struct SynapticsHwState *hw,
struct input_event *ev)
@@ -567,8 +579,20 @@ EventProcessTouchEvent(InputInfoPtr pInfo, struct SynapticsHwState *hw,
int map = proto_data->axis_map[ev->code - ABS_MT_TOUCH_MAJOR];
valuator_mask_set(hw->mt_mask[slot_index], map, ev->value);
if (slot_index >= 0)
- valuator_mask_set(proto_data->last_mt_vals[slot_index], map,
- ev->value);
+ {
+ ValuatorMask *mask = proto_data->last_mt_vals[slot_index];
+ int last_val = valuator_mask_get(mask, map);
+
+ if (EventTouchSlotPreviouslyOpen(priv, slot_index))
+ {
+ if (ev->code == ABS_MT_POSITION_X)
+ hw->cumulative_dx += ev->value - last_val;
+ else if (ev->code == ABS_MT_POSITION_Y)
+ hw->cumulative_dy += ev->value - last_val;
+ }
+
+ valuator_mask_set(mask, map, ev->value);
+ }
}
}
#endif
@@ -597,6 +621,12 @@ static int count_fingers(const struct CommData *comm)
}
+static inline double
+apply_st_scaling(struct eventcomm_proto_data *proto_data, int value, int axis)
+{
+ return value * proto_data->st_to_mt_scale[axis] + proto_data->st_to_mt_offset[axis];
+}
+
Bool
EventReadHwState(InputInfoPtr pInfo,
struct CommData *comm, struct SynapticsHwState *hwRet)
@@ -610,6 +640,13 @@ EventReadHwState(InputInfoPtr pInfo,
SynapticsResetTouchHwState(hw);
+ /* Reset cumulative values if buttons were not previously pressed */
+ if (!hw->left && !hw->right && !hw->middle)
+ {
+ hw->cumulative_dx = hw->x;
+ hw->cumulative_dy = hw->y;
+ }
+
while (SynapticsReadEvent(pInfo, &ev)) {
switch (ev.type) {
case EV_SYN:
@@ -682,12 +719,10 @@ EventReadHwState(InputInfoPtr pInfo,
if (ev.code < ABS_MT_SLOT) {
switch (ev.code) {
case ABS_X:
- hw->x = ev.value * proto_data->st_to_mt_scale_x +
- proto_data->st_to_mt_offset_x;
+ hw->x = apply_st_scaling(proto_data, ev.value, 0);
break;
case ABS_Y:
- hw->y = ev.value * proto_data->st_to_mt_scale_y +
- proto_data->st_to_mt_offset_y;
+ hw->y = apply_st_scaling(proto_data, ev.value, 1);
break;
case ABS_PRESSURE:
hw->z = ev.value;
@@ -714,6 +749,7 @@ static void
event_query_touch(InputInfoPtr pInfo)
{
SynapticsPrivate *priv = (SynapticsPrivate *)pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
struct eventcomm_proto_data *proto_data = priv->proto_data;
struct mtdev *mtdev;
int i;
@@ -728,7 +764,13 @@ event_query_touch(InputInfoPtr pInfo)
{
xf86IDrvMsg(pInfo, X_INFO,
"ignoring touch events for semi-multitouch device\n");
- return;
+ priv->has_semi_mt = TRUE;
+ }
+
+ if (rc >= 0 && BitIsOn(&prop, INPUT_PROP_BUTTONPAD))
+ {
+ xf86IDrvMsg(pInfo, X_INFO, "found clickpad property\n");
+ para->clickpad = TRUE;
}
mtdev = mtdev_new_open(pInfo->fd);
diff --git a/src/properties.c b/src/properties.c
index 0a52801..783b516 100644
--- a/src/properties.c
+++ b/src/properties.c
@@ -58,6 +58,7 @@ Atom prop_finger = 0;
Atom prop_tap_time = 0;
Atom prop_tap_move = 0;
Atom prop_tap_durations = 0;
+Atom prop_clickpad = 0;
Atom prop_tap_fast = 0;
Atom prop_middle_timeout = 0;
Atom prop_twofinger_pressure = 0;
@@ -91,6 +92,7 @@ Atom prop_gestures = 0;
Atom prop_capabilities = 0;
Atom prop_resolution = 0;
Atom prop_area = 0;
+Atom prop_softbutton_areas = 0;
Atom prop_noise_cancellation = 0;
Atom prop_product_id = 0;
Atom prop_device_node = 0;
@@ -149,6 +151,24 @@ InitFloatAtom(DeviceIntPtr dev, char *name, int nvalues, float *values)
return atom;
}
+static void
+InitSoftButtonProperty(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = (SynapticsPrivate *) pInfo->private;
+ SynapticsParameters *para = &priv->synpara;
+ int values[8];
+
+ values[0] = para->softbutton_areas[0][0];
+ values[1] = para->softbutton_areas[0][1];
+ values[2] = para->softbutton_areas[0][2];
+ values[3] = para->softbutton_areas[0][3];
+ values[4] = para->softbutton_areas[1][0];
+ values[5] = para->softbutton_areas[1][1];
+ values[6] = para->softbutton_areas[1][2];
+ values[7] = para->softbutton_areas[1][3];
+ prop_softbutton_areas = InitAtom(pInfo->dev, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 8, values);
+}
+
void
InitDeviceProperties(InputInfoPtr pInfo)
{
@@ -189,6 +209,8 @@ InitDeviceProperties(InputInfoPtr pInfo)
values[2] = para->click_time;
prop_tap_durations = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_DURATIONS, 32, 3, values);
+ prop_clickpad = InitAtom(pInfo->dev, SYNAPTICS_PROP_CLICKPAD, 8, 1,
+ &para->clickpad);
prop_tap_fast = InitAtom(pInfo->dev, SYNAPTICS_PROP_TAP_FAST, 8, 1, &para->fast_taps);
prop_middle_timeout = InitAtom(pInfo->dev, SYNAPTICS_PROP_MIDDLE_TIMEOUT,
32, 1, &para->emulate_mid_button_time);
@@ -297,6 +319,9 @@ InitDeviceProperties(InputInfoPtr pInfo)
values[3] = para->area_bottom_edge;
prop_area = InitAtom(pInfo->dev, SYNAPTICS_PROP_AREA, 32, 4, values);
+ if (para->clickpad)
+ InitSoftButtonProperty(pInfo);
+
values[0] = para->hyst_x;
values[1] = para->hyst_y;
prop_noise_cancellation = InitAtom(pInfo->dev,
@@ -392,7 +417,22 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
para->single_tap_timeout = timeouts[0];
para->tap_time_2 = timeouts[1];
para->click_time = timeouts[2];
+ } else if (property == prop_clickpad) {
+ BOOL value;
+
+ if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
+ return BadMatch;
+ value = *(BOOL*)prop->data;
+ if (!para->clickpad && value && !prop_softbutton_areas)
+ InitSoftButtonProperty(pInfo);
+ else if (para->clickpad && !value && prop_softbutton_areas)
+ {
+ XIDeleteDeviceProperty(dev, prop_softbutton_areas, FALSE);
+ prop_softbutton_areas = 0;
+ }
+
+ para->clickpad = *(BOOL*)prop->data;
} else if (property == prop_tap_fast)
{
if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
@@ -704,6 +744,19 @@ 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_softbutton_areas)
+ {
+ int *areas;
+
+ if (prop->size != 8 || prop->format != 32 || prop->type != XA_INTEGER)
+ return BadMatch;
+
+ areas = (int*)prop->data;
+ if (!SynapticsIsSoftButtonAreasValid(areas))
+ return BadValue;
+
+ memcpy(para->softbutton_areas[0], areas, 4 * sizeof(int));
+ memcpy(para->softbutton_areas[1], areas + 4, 4 * sizeof(int));
} else if (property == prop_noise_cancellation) {
INT32 *hyst;
if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
diff --git a/src/synaptics.c b/src/synaptics.c
index 2ddb733..898130e 100644
--- a/src/synaptics.c
+++ b/src/synaptics.c
@@ -429,6 +429,129 @@ static int set_percent_option(pointer options, const char* optname,
return result;
}
+Bool SynapticsIsSoftButtonAreasValid(int *values)
+{
+ Bool right_disabled = FALSE;
+ Bool middle_disabled = FALSE;
+
+ /* Check right button area */
+ if ((((values[0] != 0) && (values[1] != 0)) && (values[0] > values[1])) ||
+ (((values[2] != 0) && (values[3] != 0)) && (values[2] > values[3])))
+ return FALSE;
+
+ /* Check middle button area */
+ if ((((values[4] != 0) && (values[5] != 0)) && (values[4] > values[5])) ||
+ (((values[6] != 0) && (values[7] != 0)) && (values[6] > values[7])))
+ return FALSE;
+
+ if (values[0] == 0 && values[1] == 0 && values[2] == 0 && values[3] == 0)
+ right_disabled = TRUE;
+
+ if (values[4] == 0 && values[5] == 0 && values[6] == 0 && values[7] == 0)
+ middle_disabled = TRUE;
+
+ if (!right_disabled &&
+ ((values[0] && values[0] == values[1]) ||
+ (values[2] && values[2] == values[3])))
+ return FALSE;
+
+ if (!middle_disabled &&
+ ((values[4] && values[4] == values[5]) ||
+ (values[6] && values[6] == values[7])))
+ return FALSE;
+
+ /* Check for overlapping button areas */
+ if (!right_disabled && !middle_disabled)
+ {
+ int right_left = values[0] ? values[0] : INT_MIN;
+ int right_right = values[1] ? values[1] : INT_MAX;
+ int right_top = values[2] ? values[2] : INT_MIN;
+ int right_bottom = values[3] ? values[3] : INT_MAX;
+ int middle_left = values[4] ? values[4] : INT_MIN;
+ int middle_right = values[5] ? values[5] : INT_MAX;
+ int middle_top = values[6] ? values[6] : INT_MIN;
+ int middle_bottom = values[7] ? values[7] : INT_MAX;
+
+ /* If areas overlap in the Y axis */
+ if ((right_bottom <= middle_bottom && right_bottom >= middle_top) ||
+ (right_top <= middle_bottom && right_top >= middle_top))
+ {
+ /* Check for overlapping left edges */
+ if ((right_left < middle_left && right_right >= middle_left) ||
+ (middle_left < right_left && middle_right >= right_left))
+ return FALSE;
+
+ /* Check for overlapping right edges */
+ if ((right_right > middle_right && right_left <= middle_right) ||
+ (middle_right > right_right && middle_left <= right_right))
+ return FALSE;
+ }
+
+ /* If areas overlap in the X axis */
+ if ((right_left >= middle_left && right_left <= middle_right) ||
+ (right_right >= middle_left && right_right <= middle_right))
+ {
+ /* Check for overlapping top edges */
+ if ((right_top < middle_top && right_bottom >= middle_top) ||
+ (middle_top < right_top && middle_bottom >= right_top))
+ return FALSE;
+
+ /* Check for overlapping bottom edges */
+ if ((right_bottom > middle_bottom && right_top <= middle_bottom) ||
+ (middle_bottom > right_bottom && middle_top <= right_bottom))
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+static void set_softbutton_areas_option(InputInfoPtr pInfo)
+{
+ SynapticsPrivate *priv = pInfo->private;
+ SynapticsParameters *pars = &priv->synpara;
+ int values[8];
+ char *option_string;
+ char *next_num;
+ char *end_str;
+ int i;
+
+ if (!pars->clickpad)
+ return;
+
+ option_string = xf86CheckStrOption(pInfo->options, "SoftButtonAreas", NULL);
+ if (!option_string)
+ return;
+
+ next_num = option_string;
+
+ for (i = 0; i < 8 && *next_num != '\0'; i++)
+ {
+ long int value = strtol(next_num, &end_str, 0);
+ if (value > INT_MAX || value < -INT_MAX)
+ goto fail;
+
+ values[i] = value;
+
+ if (next_num != end_str)
+ next_num = end_str;
+ else
+ goto fail;
+ }
+
+ if (i < 8 || *next_num != '\0' || !SynapticsIsSoftButtonAreasValid(values))
+ goto fail;
+
+ memcpy(pars->softbutton_areas[0], values, 4 * sizeof(int));
+ memcpy(pars->softbutton_areas[1], values + 4, 4 * sizeof(int));
+
+ return;
+
+fail:
+ xf86IDrvMsg(pInfo, X_ERROR, "invalid SoftButtonAreas value '%s', keeping defaults\n",
+ option_string);
+}
+
static void set_default_parameters(InputInfoPtr pInfo)
{
SynapticsPrivate *priv = pInfo->private; /* read-only */
@@ -454,6 +577,7 @@ static void set_default_parameters(InputInfoPtr pInfo)
int vertResolution = 1;
int width, height, diag, range;
int horizHyst, vertHyst;
+ int middle_button_timeout;
/* read the parameters */
if (priv->synshm)
@@ -552,8 +676,11 @@ static void set_default_parameters(InputInfoPtr pInfo)
pars->tap_move = xf86SetIntOption(opts, "MaxTapMove", tapMove);
pars->tap_time_2 = xf86SetIntOption(opts, "MaxDoubleTapTime", 180);
pars->click_time = xf86SetIntOption(opts, "ClickTime", 100);
+ pars->clickpad = xf86SetIntOption(opts, "ClickPad", pars->clickpad); /* Probed */
pars->fast_taps = xf86SetBoolOption(opts, "FastTaps", FALSE);
- pars->emulate_mid_button_time = xf86SetIntOption(opts, "EmulateMidButtonTime", 75);
+ /* middle mouse button emulation on a clickpad? nah, you're joking */
+ middle_button_timeout = pars->clickpad ? 0 : 75;
+ pars->emulate_mid_button_time = xf86SetIntOption(opts, "EmulateMidButtonTime", middle_button_timeout);
pars->emulate_twofinger_z = xf86SetIntOption(opts, "EmulateTwoFingerMinZ", emulateTwoFingerMinZ);
pars->emulate_twofinger_w = xf86SetIntOption(opts, "EmulateTwoFingerMinW", emulateTwoFingerMinW);
pars->scroll_dist_vert = xf86SetIntOption(opts, "VertScrollDelta", vertScrollDelta);
@@ -619,6 +746,8 @@ static void set_default_parameters(InputInfoPtr pInfo)
pars->bottom_edge = tmp;
xf86IDrvMsg(pInfo, X_WARNING, "TopEdge is bigger than BottomEdge. Fixing.\n");
}
+
+ set_softbutton_areas_option(pInfo);
}
#if GET_ABI_MAJOR(ABI_XINPUT_VERSION) >= 14
@@ -957,6 +1086,7 @@ DeviceClose(DeviceIntPtr dev)
priv->timer = NULL;
free_shm_data(priv);
SynapticsHwStateFree(&priv->hwState);
+ SynapticsHwStateFree(&priv->old_hw_state);
SynapticsHwStateFree(&priv->local_hw_state);
SynapticsHwStateFree(&priv->comm.hwState);
return RetValue;
@@ -1231,6 +1361,10 @@ DeviceInit(DeviceIntPtr dev)
if (!priv->hwState)
goto fail;
+ priv->old_hw_state = SynapticsHwStateAlloc(priv);
+ if (!priv->old_hw_state)
+ goto fail;
+
priv->local_hw_state = SynapticsHwStateAlloc(priv);
if (!priv->local_hw_state)
goto fail;
@@ -1372,6 +1506,45 @@ is_inside_active_area(SynapticsPrivate *priv, int x, int y)
return inside_area;
}
+static Bool
+is_inside_button_area(SynapticsParameters *para, int which, int x, int y)
+{
+ Bool inside_area = TRUE;
+
+ if (para->softbutton_areas[which][0] == 0 &&
+ para->softbutton_areas[which][1] == 0 &&
+ para->softbutton_areas[which][2] == 0 &&
+ para->softbutton_areas[which][3] == 0)
+ return FALSE;
+
+ if (para->softbutton_areas[which][0] &&
+ x < para->softbutton_areas[which][0])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][1] &&
+ x > para->softbutton_areas[which][1])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][2] &&
+ y < para->softbutton_areas[which][2])
+ inside_area = FALSE;
+ else if (para->softbutton_areas[which][3] &&
+ y > para->softbutton_areas[which][3])
+ inside_area = FALSE;
+
+ return inside_area;
+}
+
+static Bool
+is_inside_rightbutton_area(SynapticsParameters *para, int x, int y)
+{
+ return is_inside_button_area(para, 0, x, y);
+}
+
+static Bool
+is_inside_middlebutton_area(SynapticsParameters *para, int x, int y)
+{
+ return is_inside_button_area(para, 1, x, y);
+}
+
static CARD32
timerFunc(OsTimerPtr timer, CARD32 now, pointer arg)
{
@@ -1428,8 +1601,17 @@ ReadInput(InputInfoPtr pInfo)
SynapticsResetTouchHwState(hw);
while (SynapticsGetHwState(pInfo, priv, hw)) {
+ /* Semi-mt device touch slots do not track touches. When there is a
+ * change in the number of touches, we must disregard the temporary
+ * motion changes. */
+ if (priv->has_semi_mt && hw->numFingers != priv->hwState->numFingers) {
+ hw->cumulative_dx = priv->hwState->cumulative_dx;
+ hw->cumulative_dy = priv->hwState->cumulative_dy;
+ }
+
SynapticsCopyHwState(priv->hwState, hw);
delay = HandleState(pInfo, hw, hw->millis, FALSE);
+ SynapticsCopyHwState(priv->old_hw_state, priv->hwState);
newDelay = TRUE;
}
@@ -1447,6 +1629,9 @@ HandleMidButtonEmulation(SynapticsPrivate *priv, struct SynapticsHwState *hw, CA
int timeleft;
int mid = 0;
+ if (para->emulate_mid_button_time <= 0)
+ return mid;
+
while (!done) {
switch (priv->mid_emu_state) {
case MBE_LEFT_CLICK:
@@ -1709,7 +1894,7 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
Bool inside_active_area)
{
SynapticsParameters *para = &priv->synpara;
- Bool touch, release, is_timeout, move;
+ Bool touch, release, is_timeout, move, press;
int timeleft, timeout;
edge_type edge;
int delay = 1000000000;
@@ -1723,6 +1908,7 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
(priv->tap_max_fingers <= ((priv->horiz_scroll_twofinger_on || priv->vert_scroll_twofinger_on)? 2 : 1)) &&
((abs(hw->x - priv->touch_on.x) >= para->tap_move) ||
(abs(hw->y - priv->touch_on.y) >= para->tap_move)));
+ press = (hw->left || hw->right || hw->middle);
if (touch) {
priv->touch_on.x = hw->x;
@@ -1745,6 +1931,10 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
SetTapState(priv, TS_1, now);
break;
case TS_1:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
if (move) {
SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
SetTapState(priv, TS_MOVE, now);
@@ -1768,6 +1958,10 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
}
break;
case TS_MOVE:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
if (move && priv->moving_state == MS_TRACKSTICK) {
SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
}
@@ -1822,6 +2016,10 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
}
break;
case TS_DRAG:
+ if (para->clickpad && press) {
+ SetTapState(priv, TS_CLICKPAD_MOVE, now);
+ goto restart;
+ }
if (move)
SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
if (release) {
@@ -1850,6 +2048,23 @@ HandleTapProcessing(SynapticsPrivate *priv, struct SynapticsHwState *hw,
SetTapState(priv, TS_START, now);
}
break;
+ case TS_CLICKPAD_MOVE:
+ /* Disable scrolling once a button is pressed on a clickpad */
+ priv->vert_scroll_edge_on = FALSE;
+ priv->horiz_scroll_edge_on = FALSE;
+ priv->vert_scroll_twofinger_on = FALSE;
+ priv->horiz_scroll_twofinger_on = FALSE;
+
+ /* Assume one touch is only for holding the clickpad button down */
+ if (hw->numFingers > 1)
+ hw->numFingers--;
+ SetMovingState(priv, MS_TOUCHPAD_RELATIVE, now);
+ if (!press) {
+ SetMovingState(priv, MS_FALSE, now);
+ SetTapState(priv, TS_MOVE, now);
+ priv->count_packet_finger = 0;
+ }
+ break;
}
timeout = GetTimeOut(priv);
@@ -2373,11 +2588,73 @@ HandleScrolling(SynapticsPrivate *priv, struct SynapticsHwState *hw,
return delay;
}
+/**
+ * Check if any 2+ fingers are close enough together to assume this is a
+ * ClickFinger action.
+ */
+static int
+clickpad_guess_clickfingers(SynapticsPrivate *priv, struct SynapticsHwState *hw)
+{
+ int nfingers = 0;
+#if HAVE_MULTITOUCH
+ int i, j;
+ for (i = 0; i < hw->num_mt_mask - 1; i++) {
+ ValuatorMask *f1;
+
+ /* you can't click on open, you're not fast enough */
+ if (hw->slot_state[i] != SLOTSTATE_UPDATE)
+ continue;
+
+ f1 = hw->mt_mask[i];
+
+ for (j = i + 1; j < hw->num_mt_mask; j++) {
+ ValuatorMask *f2;
+ double x1, x2, y1, y2;
+
+ if (hw->slot_state[j] != SLOTSTATE_UPDATE)
+ continue;
+
+ f2 = hw->mt_mask[j];
+
+ x1 = valuator_mask_get_double(f1, 0);
+ y1 = valuator_mask_get_double(f1, 1);
+
+ x2 = valuator_mask_get_double(f2, 0);
+ y2 = valuator_mask_get_double(f2, 1);
+
+ /* FIXME: fingers closer together than 30% of touchpad width, but
+ * really, this should be dependent on the touchpad size. Also,
+ * you'll need to find a touchpad that doesn't lie about it's
+ * size. Good luck. */
+ if (abs(x1 - x2) < (priv->maxx - priv->minx) * .3 &&
+ abs(y1 - y2) < (priv->maxy - priv->miny) * .3)
+ nfingers++;
+ }
+ }
+#endif
+
+ /* 1 doesn't make sense */
+ return nfingers ? nfingers + 1 : 0;
+}
+
+
static void
-handle_clickfinger(SynapticsParameters *para, struct SynapticsHwState *hw)
+handle_clickfinger(SynapticsPrivate *priv, struct SynapticsHwState *hw)
{
+ SynapticsParameters *para = &priv->synpara;
int action = 0;
- switch(hw->numFingers){
+ int nfingers = hw->numFingers;
+
+ /* if this is a clickpad, clickfinger handling is:
+ * one finger down: no action, this is a normal click
+ * two fingers down: F2_CLICK
+ * three fingers down: F3_CLICK
+ */
+
+ if (para->clickpad)
+ nfingers = clickpad_guess_clickfingers(priv, hw);
+
+ switch(nfingers) {
case 1:
action = para->click_action[F1_CLICK1];
break;
@@ -2390,15 +2667,15 @@ handle_clickfinger(SynapticsParameters *para, struct SynapticsHwState *hw)
}
switch(action){
case 1:
- hw->left = 1;
+ hw->left = 1 | BTN_EMULATED_FLAG;
break;
case 2:
hw->left = 0;
- hw->middle = 1;
+ hw->middle = 1 | BTN_EMULATED_FLAG;
break;
case 3:
hw->left = 0;
- hw->right = 1;
+ hw->right = 1 | BTN_EMULATED_FLAG;
break;
}
}
@@ -2472,7 +2749,7 @@ adjust_state_from_scrollbuttons(const InputInfoPtr pInfo, struct SynapticsHwStat
static void
update_hw_button_state(const InputInfoPtr pInfo, struct SynapticsHwState *hw,
- CARD32 now, int *delay)
+ struct SynapticsHwState *old, CARD32 now, int *delay)
{
SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
SynapticsParameters *para = &priv->synpara;
@@ -2484,9 +2761,27 @@ update_hw_button_state(const InputInfoPtr pInfo, struct SynapticsHwState *hw,
/* 3rd button emulation */
hw->middle |= HandleMidButtonEmulation(priv, hw, now, delay);
- /* Fingers emulate other buttons */
- if(hw->left && hw->numFingers >= 1){
- handle_clickfinger(para, hw);
+ /* If this is a clickpad and the user clicks in a soft button area, press
+ * the soft button instead. */
+ if (para->clickpad && hw->left && !hw->right && !hw->middle)
+ {
+ if (is_inside_rightbutton_area(para, hw->x, hw->y))
+ {
+ hw->left = 0;
+ hw->right = 1;
+ }
+ else if (is_inside_middlebutton_area(para, hw->x, hw->y))
+ {
+ hw->left = 0;
+ hw->middle = 1;
+ }
+ }
+
+ /* Fingers emulate other buttons. ClickFinger can only be
+ triggered on transition, when left is pressed
+ */
+ if(hw->left && !old->left && hw->numFingers >= 1) {
+ handle_clickfinger(priv, hw);
}
/* Two finger emulation */
@@ -2681,6 +2976,9 @@ HandleTouches(InputInfoPtr pInfo, struct SynapticsHwState *hw)
new_active_touches--;
}
+ if (priv->has_semi_mt)
+ goto out;
+
if (priv->num_active_touches < min_touches &&
new_active_touches < min_touches)
{
@@ -2740,6 +3038,27 @@ out:
#endif
}
+static void
+filter_jitter(SynapticsPrivate *priv, int *x, int *y)
+{
+ SynapticsParameters *para = &priv->synpara;
+
+ priv->hyst_center_x = hysteresis(*x, priv->hyst_center_x, para->hyst_x);
+ priv->hyst_center_y = hysteresis(*y, priv->hyst_center_y, para->hyst_y);
+ *x = priv->hyst_center_x;
+ *y = priv->hyst_center_y;
+}
+
+static void
+reset_hw_state(struct SynapticsHwState *hw)
+{
+ hw->x = 0;
+ hw->y = 0;
+ hw->z = 0;
+ hw->numFingers = 0;
+ hw->fingerWidth = 0;
+}
+
/*
* React on changes in the hardware state. This function is called every time
* the hardware state changes. The return value is used to specify how many
@@ -2756,8 +3075,8 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
{
SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
SynapticsParameters *para = &priv->synpara;
- enum FingerState finger;
- int dx, dy, buttons, id;
+ enum FingerState finger = FS_UNTOUCHED;
+ int dx = 0, dy = 0, buttons, id;
edge_type edge = NO_EDGE;
int change;
int double_click = FALSE;
@@ -2774,13 +3093,18 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
return delay;
}
+ /* If a physical button is pressed on a clickpad, use cumulative relative
+ * touch movements for motion */
+ if (para->clickpad && (hw->left || hw->right || hw->middle))
+ {
+ hw->x = hw->cumulative_dx;
+ hw->y = hw->cumulative_dy;
+ }
+
/* apply hysteresis before doing anything serious. This cancels
* out a lot of noise which might surface in strange phenomena
* like flicker in scrolling or noise motion. */
- priv->hyst_center_x = hysteresis(hw->x, priv->hyst_center_x, para->hyst_x);
- priv->hyst_center_y = hysteresis(hw->y, priv->hyst_center_y, para->hyst_y);
- hw->x = priv->hyst_center_x;
- hw->y = priv->hyst_center_y;
+ filter_jitter(priv, &hw->x, &hw->y);
inside_active_area = is_inside_active_area(priv, hw->x, hw->y);
@@ -2790,22 +3114,14 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
*/
if (!inside_active_area)
{
- hw->x = 0;
- hw->y = 0;
- hw->z = 0;
- hw->numFingers = 0;
- hw->fingerWidth = 0;
+ reset_hw_state(hw);
/* FIXME: if finger accidentally moves into the area and doesn't
* really release, the finger should remain down. */
- finger = FS_UNTOUCHED;
- edge = NO_EDGE;
-
- dx = dy = 0;
}
/* these two just update hw->left, right, etc. */
- update_hw_button_state(pInfo, hw, now, &delay);
+ update_hw_button_state(pInfo, hw, priv->old_hw_state, now, &delay);
if (priv->has_scrollbuttons)
double_click = adjust_state_from_scrollbuttons(pInfo, hw);
diff --git a/src/synapticsstr.h b/src/synapticsstr.h
index 92c64fb..fcefc46 100644
--- a/src/synapticsstr.h
+++ b/src/synapticsstr.h
@@ -99,7 +99,8 @@ enum TapState {
TS_3, /* After second touch */
TS_DRAG, /* Pointer drag enabled */
TS_4, /* After release when "locked drags" enabled */
- TS_5 /* After touch when "locked drags" enabled */
+ TS_5, /* After touch when "locked drags" enabled */
+ TS_CLICKPAD_MOVE, /* After left button press on a clickpad */
};
enum TapButtonState {
@@ -126,6 +127,7 @@ typedef struct _SynapticsParameters
int single_tap_timeout; /* timeout to recognize a single tap */
int tap_time_2; /* max. tapping time for double taps */
int click_time; /* The duration of a single click */
+ Bool clickpad; /* Device is a has integrated buttons */
Bool fast_taps; /* Faster reaction to single taps */
int emulate_mid_button_time; /* Max time between left and right button presses to
emulate a middle button press. */
@@ -179,6 +181,7 @@ 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 softbutton_areas[2][4]; /* soft button area coordinates, 0 => right, 1 => middle button */
int hyst_x, hyst_y; /* x and y width of hysteresis box */
} SynapticsParameters;
@@ -193,6 +196,7 @@ struct _SynapticsPrivateRec
void *proto_data; /* protocol-specific data */
struct SynapticsHwState *hwState;
+ struct SynapticsHwState *old_hw_state; /* previous hw state */
const char *device; /* device node */
Bool shm_config; /* True when shared memory area allocated */
@@ -266,6 +270,7 @@ struct _SynapticsPrivateRec
Bool has_pressure; /* device reports pressure */
Bool has_width; /* device reports finger width */
Bool has_scrollbuttons; /* device has physical scrollbuttons */
+ Bool has_semi_mt; /* device is only semi-multitouch capable */
enum TouchpadModel model; /* The detected model */
unsigned short id_vendor; /* vendor id */
diff --git a/src/synproto.c b/src/synproto.c
index 0426e8f..bdf2d21 100644
--- a/src/synproto.c
+++ b/src/synproto.c
@@ -120,14 +120,16 @@ SynapticsCopyHwState(struct SynapticsHwState *dst,
dst->x = src->x;
dst->y = src->y;
dst->z = src->z;
+ dst->cumulative_dx = src->cumulative_dx;
+ dst->cumulative_dy = src->cumulative_dy;
dst->numFingers = src->numFingers;
dst->fingerWidth = src->fingerWidth;
- dst->left = src->left;
- dst->right = src->right;
+ dst->left = src->left & BTN_EMULATED_FLAG ? 0 : src->left;
+ dst->right = src->right & BTN_EMULATED_FLAG ? 0 : src->right;
dst->up = src->up;
dst->down = src->down;
memcpy(dst->multi, src->multi, sizeof(dst->multi));
- dst->middle = src->middle;
+ dst->middle = src->middle & BTN_EMULATED_FLAG ? 0 : src->middle;
#ifdef HAVE_MULTITOUCH
for (i = 0; i < dst->num_mt_mask && i < src->num_mt_mask; i++)
valuator_mask_copy(dst->mt_mask[i], src->mt_mask[i]);
diff --git a/src/synproto.h b/src/synproto.h
index 89392ac..95ebc92 100644
--- a/src/synproto.h
+++ b/src/synproto.h
@@ -45,6 +45,9 @@ enum SynapticsSlotState
SLOTSTATE_UPDATE,
};
+/* used to mark emulated hw button state */
+#define BTN_EMULATED_FLAG 0x80
+
/*
* A structure to describe the state of the touchpad hardware (buttons and pad)
*/
@@ -53,6 +56,8 @@ struct SynapticsHwState {
int x; /* X position of finger */
int y; /* Y position of finger */
int z; /* Finger pressure */
+ int cumulative_dx; /* Cumulative delta X for clickpad dragging */
+ int cumulative_dy; /* Cumulative delta Y for clickpad dragging */
int numFingers;
int fingerWidth;
@@ -115,4 +120,6 @@ extern void SynapticsCopyHwState(struct SynapticsHwState *dst,
const struct SynapticsHwState *src);
extern void SynapticsResetTouchHwState(struct SynapticsHwState *hw);
+extern Bool SynapticsIsSoftButtonAreasValid(int *values);
+
#endif /* _SYNPROTO_H_ */
diff --git a/test/fake-symbols.c b/test/fake-symbols.c
index 7f3f0ac..65fad46 100644
--- a/test/fake-symbols.c
+++ b/test/fake-symbols.c
@@ -461,6 +461,11 @@ _X_EXPORT void valuator_mask_free(ValuatorMask **mask)
{
}
+_X_EXPORT int valuator_mask_get(const ValuatorMask *mask, int valuator)
+{
+ return 0;
+}
+
_X_EXPORT void valuator_mask_set(ValuatorMask *mask, int valuator, int data)
{
}
diff --git a/tools/synclient.c b/tools/synclient.c
index 14ec605..942312a 100644
--- a/tools/synclient.c
+++ b/tools/synclient.c
@@ -38,6 +38,7 @@
#include <string.h>
#include <stddef.h>
#include <math.h>
+#include <limits.h>
#include <X11/Xdefs.h>
#include <X11/Xatom.h>
@@ -144,6 +145,15 @@ static struct Parameter params[] = {
{"AreaBottomEdge", PT_INT, 0, 10000, SYNAPTICS_PROP_AREA, 32, 3},
{"HorizHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 0},
{"VertHysteresis", PT_INT, 0, 10000, SYNAPTICS_PROP_NOISE_CANCELLATION, 32, 1},
+ {"ClickPad", PT_BOOL, 0, 1, SYNAPTICS_PROP_CLICKPAD, 8, 0},
+ {"RightButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 0},
+ {"RightButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 1},
+ {"RightButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 2},
+ {"RightButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 3},
+ {"MiddleButtonAreaLeft", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 4},
+ {"MiddleButtonAreaRight", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 5},
+ {"MiddleButtonAreaTop", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 6},
+ {"MiddleButtonAreaBottom", PT_INT, INT_MIN, INT_MAX, SYNAPTICS_PROP_SOFTBUTTON_AREAS, 32, 7},
{ NULL, 0, 0, 0, 0 }
};