summaryrefslogtreecommitdiff
path: root/src/synaptics.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/synaptics.c')
-rw-r--r--src/synaptics.c370
1 files changed, 343 insertions, 27 deletions
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);