[PATCH] [synaptic] swipe actions based on the tree-fingers touch

Peter Hutterer peter.hutterer at who-t.net
Thu Sep 4 21:10:30 PDT 2014


Hi Arkadiusz,

sorry about the delay on the review here.

On Fri, Apr 25, 2014 at 07:31:44PM +0200, Arkadiusz Bokowy wrote:
> When touchpad can report more then 2 active fingers, we can generate action
> based on swipe gesture. It includes upward, downward, to the left and to the
> right swipes. To all of those gestures can be assigned one button event. By
> default (internal driver default) swipes are disabled (assigned button 0).
> The swipe length (hand displacement) required for triggering button event can
> be set independently for horizontal and vertical movement. There is also
> possibility for triggering repeatable events during swipe handling, however
> it is disabled by default.
> 
> The most preferable button assignment is button 8, 9, 10 and 11 for to the
> left, to the right, upward and downward swipes respectively. Such a
> configuration provides browser history navigation (e.g. Firefox) with
> horizontal swipes. Button 10 and 11 are not used, though.
> 
> Added Synaptic driver options:
> * SwipeUpButton, SwipeDownButton, SwipeLeftButton, SwipeRightButton
> * HorizSwipeThreshold, VertSwipeThreshold
> * SingleSwipe
> 
> This changes was developed on Samsung NP530U3C with the ETPS/2 Elantech
> Touchpad, which can report up to three-finger touch. Also tested on the
> SynPS/2 Synaptics TouchPad. Patch is generated for the driver version 1.7.4.
> 
> Signed-off-by: Arkadiusz Bokowy <arkadiusz.bokowy at gmail.com>
> ---
>  include/synaptics-properties.h | 10 +++++
>  man/synaptics.man              | 28 ++++++++++++
>  src/properties.c               | 44 +++++++++++++++++++
>  src/synaptics.c                | 99 +++++++++++++++++++++++++++++++++++++++++-
>  src/synapticsstr.h             | 21 +++++++++
>  tools/synclient.c              |  7 +++
>  6 files changed, 207 insertions(+), 2 deletions(-)
> 
> diff --git a/include/synaptics-properties.h b/include/synaptics-properties.h
> index 19bd2b2..e74ffd0 100644
> --- a/include/synaptics-properties.h
> +++ b/include/synaptics-properties.h
> @@ -104,6 +104,16 @@
>   * element. order: Finger 1, 2, 3 */
>  #define SYNAPTICS_PROP_CLICK_ACTION "Synaptics Click Action"
>  
> +/* 8 bit, up to MAX_SWIPE values (see synaptics.h), 0 disables an
> + * element. order: Up, Down, Left, Right */
> +#define SYNAPTICS_PROP_SWIPE_ACTION "Synaptics Swipe Action"

no references to header files please. Just say there are 4 values in it.

> +
> +/* 8 bit, 2 values, vertical, horizontal */
> +#define SYNAPTICS_PROP_SWIPE_THRESHOLD "Synaptics Swipe Threshold"
> +
> +/* 8 bit (BOOL) */
> +#define SYNAPTICS_PROP_SINGLE_SWIPE "Synaptics Single Swipe"

I don't think we need this one. I'm ok with adding support for three-finger
swipe but if we only map them to buttons anyway, a single button per gesture
is enough. The only time where multiple buttons are useful is for scroll
buttons and that's where we already have two-finger scrolling for.

that brings up the next question: should we even support vertical scroll. in
X, due to a number of limitations the only buttons that make sense to map to
are 8/9 for back/forward. Some specialised apps may be able to use 10/11
etc. but I'd rather have a small limited feature that has predictable
outcomes.

so I'm in favour of dropping the single swipe toggle, up/down and, quite
frankly, four finger swiping. Unless gabriele comes up with a really good
use-case, just exposing what the hw can do isn't enough tbh.

> +
>  /* 8 bit (BOOL) */
>  #define SYNAPTICS_PROP_CIRCULAR_SCROLLING "Synaptics Circular Scrolling"
>  
> diff --git a/man/synaptics.man b/man/synaptics.man
> index 7da7527..d3e4fb3 100644
> --- a/man/synaptics.man
> +++ b/man/synaptics.man
> @@ -358,6 +358,34 @@ Left/Right/Top/BottomEdge parameters.
>  .
>  For circular touchpads. Property: "Synaptics Circular Pad"
>  .TP
> +.BI "Option \*qSwipeUpButton\*q \*q" integer \*q
> +Which mouse button is reported on a upward tree-finger swipe. Property:
> +"Synaptics Swipe Action"

see Gabriele's patch, this should have Three in the name to be less
ambiuous.

> +.TP
> +.BI "Option \*qSwipeDownButton\*q \*q" integer \*q
> +Which mouse button is reported on a downward tree-finger swipe. Property:
> +"Synaptics Swipe Action"
> +.TP
> +.BI "Option \*qSwipeLeftButton\*q \*q" integer \*q
> +Which mouse button is reported on a tree-finger swipe to the left. Property:
> +"Synaptics Swipe Action"
> +.TP
> +.BI "Option \*qSwipeRightButton\*q \*q" integer \*q
> +Which mouse button is reported on a tree-finger swipe to the right. Property:
> +"Synaptics Swipe Action"
> +.TP
> +.BI "Option \*qHorizSwipeThreshold\*q \*q" integer \*q
> +Horizontal threshold for triggering swipe event. Property: "Synaptics Swipe
> +Threshold"
> +.TP
> +.BI "Option \*qVertSwipeThreshold\*q \*q" integer \*q
> +Vertical threshold for triggering swipe event. Property: "Synaptics Swipe
> +Threshold"

both thresholds need a unit specification.

The rest looks good, but as I said above I think we should limit this patch
to do a lot less.

Cheers,
   Peter

> +.TP
> +.BI "Option \*qSingleSwipe\*q \*q" boolean \*q
> +Trigger only one button click event per swipe. Property: "Synaptics Single
> +Swipe"
> +.TP
>  .BI "Option \*qPalmDetect\*q \*q" boolean \*q
>  If palm detection should be enabled.
>  .
> diff --git a/src/properties.c b/src/properties.c
> index 886d8c2..140cf6e 100644
> --- a/src/properties.c
> +++ b/src/properties.c
> @@ -80,6 +80,9 @@ Atom prop_circscroll = 0;
>  Atom prop_circscroll_dist = 0;
>  Atom prop_circscroll_trigger = 0;
>  Atom prop_circpad = 0;
> +Atom prop_swipeaction = 0;
> +Atom prop_swipethreshold = 0;
> +Atom prop_singleswipe = 0;
>  Atom prop_palm = 0;
>  Atom prop_palm_dim = 0;
>  Atom prop_coastspeed = 0;
> @@ -298,6 +301,18 @@ InitDeviceProperties(InputInfoPtr pInfo)
>      prop_circpad =
>          InitAtom(pInfo->dev, SYNAPTICS_PROP_CIRCULAR_PAD, 8, 1,
>                   &para->circular_pad);
> +
> +    memcpy(values, para->swipe_action, MAX_SWIPE * sizeof(int));
> +    prop_swipeaction =
> +        InitAtom(pInfo->dev, SYNAPTICS_PROP_SWIPE_ACTION, 8, MAX_SWIPE, values);
> +    values[0] = para->swipe_threshold_x;
> +    values[1] = para->swipe_threshold_y;
> +    prop_swipethreshold =
> +        InitAtom(pInfo->dev, SYNAPTICS_PROP_SWIPE_THRESHOLD, 32, 2, values);
> +    prop_singleswipe =
> +        InitAtom(pInfo->dev, SYNAPTICS_PROP_SINGLE_SWIPE, 8, 1,
> +                 &para->single_swipe);
> +
>      prop_palm =
>          InitAtom(pInfo->dev, SYNAPTICS_PROP_PALM_DETECT, 8, 1,
>                   &para->palm_detect);
> @@ -675,6 +690,35 @@ SetProperty(DeviceIntPtr dev, Atom property, XIPropertyValuePtr prop,
>  
>          para->circular_pad = *(BOOL *) prop->data;
>      }
> +    else if (property == prop_swipeaction) {
> +        int i;
> +        CARD8 *action;
> +
> +        if (prop->size > MAX_SWIPE || prop->format != 8 ||
> +            prop->type != XA_INTEGER)
> +            return BadMatch;
> +
> +        action = (CARD8 *) prop->data;
> +
> +        for (i = 0; i < MAX_SWIPE; i++)
> +            para->swipe_action[i] = action[i];
> +    }
> +    else if (property == prop_swipethreshold) {
> +        INT32 *swipe_thresholds;
> +
> +        if (prop->size != 2 || prop->format != 32 || prop->type != XA_INTEGER)
> +            return BadMatch;
> +
> +        swipe_thresholds = (INT32 *) prop->data;
> +        para->swipe_threshold_x = swipe_thresholds[0];
> +        para->swipe_threshold_y = swipe_thresholds[1];
> +    }
> +    else if (property == prop_singleswipe) {
> +        if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
> +            return BadMatch;
> +
> +        para->single_swipe = *(BOOL *) prop->data;
> +    }
>      else if (property == prop_palm) {
>          if (prop->size != 1 || prop->format != 8 || prop->type != XA_INTEGER)
>              return BadMatch;
> diff --git a/src/synaptics.c b/src/synaptics.c
> index 0986e20..ea6c7e3 100644
> --- a/src/synaptics.c
> +++ b/src/synaptics.c
> @@ -548,6 +548,7 @@ set_default_parameters(InputInfoPtr pInfo)
>      Bool vertTwoFingerScroll, horizTwoFingerScroll;
>      int horizResolution = 1;
>      int vertResolution = 1;
> +    int horizSwipeThreshold, vertSwipeThreshold;
>      int width, height, diag, range;
>      int horizHyst, vertHyst;
>      int middle_button_timeout;
> @@ -614,6 +615,12 @@ set_default_parameters(InputInfoPtr pInfo)
>      vertTwoFingerScroll = priv->has_double ? TRUE : FALSE;
>      horizTwoFingerScroll = FALSE;
>  
> +    /* Calculate the minimal required swipe distance for horizontal and
> +     * vertical events. Default is 15 and 25 per cent of the width and
> +     * the height respectively. */
> +    horizSwipeThreshold = width * 0.15;
> +    vertSwipeThreshold = height * 0.25;

why two different values here?

> +
>      /* Use resolution reported by hardware if available */
>      if ((priv->resx > 0) && (priv->resy > 0)) {
>          horizResolution = priv->resx;
> @@ -702,6 +709,19 @@ set_default_parameters(InputInfoPtr pInfo)
>          xf86SetBoolOption(opts, "CircularScrolling", FALSE);
>      pars->circular_trigger = xf86SetIntOption(opts, "CircScrollTrigger", 0);
>      pars->circular_pad = xf86SetBoolOption(opts, "CircularPad", FALSE);
> +    pars->swipe_action[UP_SWIPE] =
> +        xf86SetIntOption(opts, "SwipeUpButton", 0);
> +    pars->swipe_action[DOWN_SWIPE] =
> +        xf86SetIntOption(opts, "SwipeDownButton", 0);
> +    pars->swipe_action[LEFT_SWIPE] =
> +        xf86SetIntOption(opts, "SwipeLeftButton", 0);
> +    pars->swipe_action[RIGHT_SWIPE] =
> +        xf86SetIntOption(opts, "SwipeRightButton", 0);
> +    pars->swipe_threshold_x =
> +        xf86SetIntOption(opts, "HorizSwipeThreshold", horizSwipeThreshold);
> +    pars->swipe_threshold_y =
> +        xf86SetIntOption(opts, "VertSwipeThreshold", vertSwipeThreshold);
> +    pars->single_swipe = xf86SetBoolOption(opts, "SingleSwipe", TRUE);
>      pars->palm_detect = xf86SetBoolOption(opts, "PalmDetect", FALSE);
>      pars->palm_min_width = xf86SetIntOption(opts, "PalmMinWidth", palmMinWidth);
>      pars->palm_min_z = xf86SetIntOption(opts, "PalmMinZ", palmMinZ);
> @@ -1862,10 +1882,13 @@ HandleTapProcessing(SynapticsPrivate * priv, struct SynapticsHwState *hw,
>  
>      touch = finger >= FS_TOUCHED && priv->finger_state == FS_UNTOUCHED;
>      release = finger == FS_UNTOUCHED && priv->finger_state >= FS_TOUCHED;
> +    /* TODO: better way to determine how many fingers was actually used
> +     * for gesture - there could be up to 5 fingers (or 6 ?) */
>      move = (finger >= FS_TOUCHED &&
>              (priv->tap_max_fingers <=
> -             ((priv->horiz_scroll_twofinger_on ||
> -               priv->vert_scroll_twofinger_on) ? 2 : 1)) &&
> +             (priv->swipe.threefinger_on ? 3 :
> +              ((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);
> @@ -2528,6 +2551,35 @@ HandleScrolling(SynapticsPrivate * priv, struct SynapticsHwState *hw,
>      return delay;
>  }
>  
> +static void
> +HandleSwipe(SynapticsPrivate *priv, struct SynapticsHwState *hw, Bool finger)
> +{
> +    SynapticsParameters *para = &priv->synpara;
> +
> +    /* not enough fingers touched, clear swipe tracking */
> +    if (!finger || hw->numFingers < 3) {
> +        priv->swipe.threefinger_on = FALSE;
> +        priv->swipe.fourfinger_on = FALSE;
> +        priv->swipe.posted = FALSE;
> +        return;
> +    }
> +
> +    if (priv->swipe.threefinger_on || priv->swipe.fourfinger_on) {
> +        /* swipe was activated, accumulate delta */
> +        priv->swipe.delta_x += hw->x - priv->swipe.last_x;
> +        priv->swipe.delta_y += hw->y - priv->swipe.last_y;
> +    }
> +    else {
> +        priv->swipe.threefinger_on = hw->numFingers == 3;
> +        priv->swipe.fourfinger_on = hw->numFingers == 4;
> +        priv->swipe.delta_x = 0;
> +        priv->swipe.delta_y = 0;
> +    }
> +
> +    priv->swipe.last_x = hw->x;
> +    priv->swipe.last_y = hw->y;
> +}
> +
>  /**
>   * Check if any 2+ fingers are close enough together to assume this is a
>   * ClickFinger action.
> @@ -2808,6 +2860,43 @@ repeat_scrollbuttons(const InputInfoPtr pInfo,
>      return delay;
>  }
>  
> +static void
> +post_swipe_events(const InputInfoPtr pInfo) {
> +    SynapticsPrivate *priv = (SynapticsPrivate *) (pInfo->private);
> +    SynapticsParameters *para = &priv->synpara;
> +
> +    /* there is no need to go any further if those conditions are not met */
> +    if (!(priv->swipe.threefinger_on || priv->swipe.fourfinger_on) ||
> +         (priv->swipe.posted && para->single_swipe))
> +      return;
> +
> +    /* swipes are intrinsically related - one big if stack */
> +    if (para->swipe_action[UP_SWIPE] &&
> +        priv->swipe.delta_y < -para->swipe_threshold_y) {
> +        post_button_click(pInfo, para->swipe_action[UP_SWIPE]);
> +        priv->swipe.posted = TRUE;
> +        priv->swipe.delta_y = 0;
> +    }
> +    else if (para->swipe_action[DOWN_SWIPE] &&
> +        priv->swipe.delta_y > para->swipe_threshold_y) {
> +        post_button_click(pInfo, para->swipe_action[DOWN_SWIPE]);
> +        priv->swipe.posted = TRUE;
> +        priv->swipe.delta_y = 0;
> +    }
> +    else if (para->swipe_action[LEFT_SWIPE] &&
> +        priv->swipe.delta_x < -para->swipe_threshold_x) {
> +        post_button_click(pInfo, para->swipe_action[LEFT_SWIPE]);
> +        priv->swipe.posted = TRUE;
> +        priv->swipe.delta_x = 0;
> +    }
> +    else if (para->swipe_action[RIGHT_SWIPE] &&
> +        priv->swipe.delta_x > para->swipe_threshold_x) {
> +        post_button_click(pInfo, para->swipe_action[RIGHT_SWIPE]);
> +        priv->swipe.posted = TRUE;
> +        priv->swipe.delta_x = 0;
> +    }
> +}
> +
>  /* Update the open slots and number of active touches */
>  static void
>  UpdateTouchState(InputInfoPtr pInfo, struct SynapticsHwState *hw)
> @@ -3039,6 +3128,8 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
>          if (timeleft > 0)
>              delay = MIN(delay, timeleft);
>  
> +        HandleSwipe(priv, hw, (finger >= FS_TOUCHED));
> +
>          /*
>           * Compensate for unequal x/y resolution. This needs to be done after
>           * calculations that require unadjusted coordinates, for example edge
> @@ -3109,6 +3200,10 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState *hw, CARD32 now,
>          priv->scroll.last_millis = hw->millis;
>      }
>  
> +    /* Process swipe events only in the active area */
> +    if (inside_active_area)
> +        post_swipe_events(pInfo);
> +
>      if (double_click) {
>          post_button_click(pInfo, 1);
>          post_button_click(pInfo, 1);
> diff --git a/src/synapticsstr.h b/src/synapticsstr.h
> index 54bc154..8135fb6 100644
> --- a/src/synapticsstr.h
> +++ b/src/synapticsstr.h
> @@ -83,6 +83,14 @@ enum ClickFingerEvent {
>      MAX_CLICK
>  };
>  
> +enum SwipeEvent {
> +    UP_SWIPE = 0,               /* Swipe upward, three fingers */
> +    DOWN_SWIPE,                 /* Swipe downward, three fingers */
> +    LEFT_SWIPE,                 /* Swipe to the left, three fingers */
> +    RIGHT_SWIPE,                /* Swipe to the right, three fingers */
> +    MAX_SWIPE
> +};
> +
>  
>  typedef struct _SynapticsMoveHist {
>      int x, y;
> @@ -187,6 +195,10 @@ typedef struct _SynapticsParameters {
>      int locked_drag_time;       /* timeout for locked drags */
>      int tap_action[MAX_TAP];    /* Button to report on tap events */
>      int click_action[MAX_CLICK];        /* Button to report on click with fingers */
> +    int swipe_action[MAX_SWIPE];        /* Button to report on swipe action */
> +    int swipe_threshold_x;      /* Threshold for horizontal swipe event */
> +    int swipe_threshold_y;      /* Threshold for vertical swipe event */
> +    Bool single_swipe;          /* Allow only one action per swipe */
>      Bool circular_scrolling;    /* Enable circular scrolling */
>      double scroll_dist_circ;    /* Scrolling angle radians */
>      int circular_trigger;       /* Trigger area for circular scrolling */
> @@ -243,6 +255,15 @@ struct _SynapticsPrivateRec {
>          double coast_delta_y;   /* Accumulated vertical coast delta */
>          int packets_this_scroll;        /* Events received for this scroll */
>      } scroll;
> +    struct {
> +        int last_x;             /* last x-swipe position */
> +        int last_y;             /* last y-swipe position */
> +        double delta_x;         /* accumulated horiz swipe delta */
> +        double delta_y;         /* accumulated vert swipe delta */
> +        Bool posted;            /* indicate that event was posted */
> +        Bool threefinger_on;    /* swipe event with three fingers */
> +        Bool fourfinger_on;     /* XXX: ?? swipe event with four fingers */
> +    } swipe;
>      int count_packet_finger;    /* packet counter with finger on the touchpad */
>      int button_delay_millis;    /* button delay for 3rd button emulation */
>      Bool prev_up;               /* Previous up button value, for double click emulation */
> diff --git a/tools/synclient.c b/tools/synclient.c
> index ac31a66..94dad62 100644
> --- a/tools/synclient.c
> +++ b/tools/synclient.c
> @@ -122,6 +122,13 @@ static struct Parameter params[] = {
>      {"CircScrollDelta",       PT_DOUBLE, .01, 3,   SYNAPTICS_PROP_CIRCULAR_SCROLLING_DIST,	0 /* float */,	0},
>      {"CircScrollTrigger",     PT_INT,    0, 8,     SYNAPTICS_PROP_CIRCULAR_SCROLLING_TRIGGER,	8,	0},
>      {"CircularPad",           PT_BOOL,   0, 1,     SYNAPTICS_PROP_CIRCULAR_PAD,	8,	0},
> +    {"SwipeUpButton",         PT_INT,    0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_SWIPE_ACTION,	8, 0},
> +    {"SwipeDownButton",       PT_INT,    0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_SWIPE_ACTION,	8, 1},
> +    {"SwipeLeftButton",       PT_INT,    0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_SWIPE_ACTION,	8, 2},
> +    {"SwipeRightButton",      PT_INT,    0, SYN_MAX_BUTTONS, SYNAPTICS_PROP_SWIPE_ACTION,	8, 3},
> +    {"HorizSwipeThreshold",   PT_INT,    0, 10000, SYNAPTICS_PROP_SWIPE_THRESHOLD, 32, 0},
> +    {"VertSwipeThreshold",    PT_INT,    0, 10000, SYNAPTICS_PROP_SWIPE_THRESHOLD, 32, 1},
> +    {"SingleSwipe",           PT_BOOL,   0, 1,     SYNAPTICS_PROP_SINGLE_SWIPE,	8, 0},
>      {"PalmDetect",            PT_BOOL,   0, 1,     SYNAPTICS_PROP_PALM_DETECT,	8,	0},
>      {"PalmMinWidth",          PT_INT,    0, 15,    SYNAPTICS_PROP_PALM_DIMENSIONS,	32,	0},
>      {"PalmMinZ",              PT_INT,    0, 255,   SYNAPTICS_PROP_PALM_DIMENSIONS,	32,	1},
> -- 
> 1.8.3.2
> _______________________________________________
> xorg-devel at lists.x.org: X.Org development
> Archives: http://lists.x.org/archives/xorg-devel
> Info: http://lists.x.org/mailman/listinfo/xorg-devel
> 


More information about the xorg-devel mailing list