On Fri, Oct 08, 2010 at 07:22:34PM +0200, Takashi Iwai wrote:
> Signed-off-by: Takashi Iwai <[email protected]>
> ---
>  src/eventcomm.c    |   56 +++++++++++++++++++--
>  src/synaptics.c    |  136 
> ++++++++++++++++++++++++++++++++++++++++++++++++----
>  src/synapticsstr.h |   12 +++++
>  src/synproto.h     |    6 ++
>  4 files changed, 196 insertions(+), 14 deletions(-)
> 
> diff --git a/src/eventcomm.c b/src/eventcomm.c
> index 76ff69d..5969448 100644
> --- a/src/eventcomm.c
> +++ b/src/eventcomm.c
> @@ -53,6 +53,17 @@
>  
>  #define SYNAPTICS_LED_SYS_FILE       
> "/sys/class/leds/psmouse::synaptics/brightness"
>  
> +#ifndef SYN_MT_REPORT
> +#define SYN_MT_REPORT                2
> +#endif
> +#ifndef ABS_MT_POSITION_X
> +#define ABS_MT_POSITION_X    0x35
> +#define ABS_MT_POSITION_Y    0x36
> +#endif
> +#ifndef ABS_MT_PRESSURE
> +#define ABS_MT_PRESSURE              0x3a
> +#endif
> +
>  
> /*****************************************************************************
>   *   Function Definitions
>   
> ****************************************************************************/
> @@ -168,6 +179,22 @@ event_query_info(InputInfoPtr pInfo)
>      }
>  }
>  
> +static void event_query_multi_touch(LocalDevicePtr local)
> +{
> +    SynapticsPrivate *priv = (SynapticsPrivate *)local->private;
> +    unsigned long absbits[NBITS(ABS_MAX)] = {0};
> +    int rc;
> +
> +    priv->can_multi_touch = FALSE;

has_multitouch would be more in line with the has_left, has_right, etc.

> +    if (priv->model != MODEL_SYNAPTICS)
> +     return;
> +    SYSCALL(rc = ioctl(local->fd, EVIOCGBIT(EV_ABS, sizeof(absbits)), 
> absbits));
> +    if (rc >= 0 && TEST_BIT(ABS_MT_POSITION_X, absbits)) {
> +     priv->can_multi_touch = TRUE;
> +     xf86Msg(X_INFO, "%s: supports multi-touch finger detection\n", 
> local->name);
> +    }
> +}
> +

I think we should just rely on MT_SLOTS here and wrap devices that don't do
it with mtdev. It'll likely make the code simpler (especially in regards to
tracking the primary) and I can blame Henrik if the tracking doesn't work ;)


>  static void
>  event_query_clickpad(LocalDevicePtr local)
>  {
> @@ -175,7 +202,7 @@ event_query_clickpad(LocalDevicePtr local)
>  
>      /* clickpad device reports only the single left button mask */
>      if (priv->has_left && !priv->has_right && !priv->has_middle &&
> -     !priv->has_double &&
> +     (!priv->has_double || priv->can_multi_touch) &&
>       priv->model == MODEL_SYNAPTICS) {
>       priv->is_clickpad = TRUE;
>       /* enable right/middle button caps; otherwise gnome-settings-daemon
> @@ -383,21 +410,27 @@ EventReadHwState(InputInfoPtr pInfo,
>       switch (ev.type) {
>       case EV_SYN:
>           switch (ev.code) {
> +         case SYN_MT_REPORT:
> +             hw->multi_touch_count++;
> +             break;
>           case SYN_REPORT:
>               if (comm->oneFinger)
> -                 hw->numFingers = 1;
> +                 hw->numFingers = hw->multi_touch_count ? 
> hw->multi_touch_count : 1;
>               else if (comm->twoFingers)
>                   hw->numFingers = 2;
>               else if (comm->threeFingers)
>                   hw->numFingers = 3;
>               else
>                   hw->numFingers = 0;
> +             hw->multi_touch = hw->multi_touch_count;
> +             hw->multi_touch_count = 0;
>               /* if the coord is out of range, we filter it out */
>               if (priv->is_clickpad && hw->z > 0 && (hw->x < minx || hw->x > 
> maxx || hw->y < miny || hw->y > maxy))
>                       return FALSE;
>               *hwRet = *hw;
>               return TRUE;
>           }
> +         break;
>       case EV_KEY:
>           v = (ev.value ? TRUE : FALSE);
>           switch (ev.code) {
> @@ -458,13 +491,25 @@ EventReadHwState(InputInfoPtr pInfo,
>       case EV_ABS:
>           switch (ev.code) {
>           case ABS_X:
> -             hw->x = ev.value;
> +         case ABS_MT_POSITION_X:
> +             if (hw->multi_touch_count)
> +                 hw->multi_touch_x = ev.value;
> +             else
> +                 hw->x = ev.value;

if I read this correctly this patch doesn't add multi-touch support but only
two-finger support, otherwise you'd be overwriting the value after the
second finger.

a different alternative to this would be to buffer all MT events in a
hw->mt[touchpoint] array of structs and then transfer the first to
hw->x/y/z at the end of HandleState/EV_SYN. it would greatly reduce the
number of if (multitouch) and improve readability.

but aside from this, this patch needs to be split into the actual MT
handling and the clickpad-related area handling. as for gestures in the
driver - see my other email.

Cheers,
  Peter
>               break;
>           case ABS_Y:
> -             hw->y = ev.value;
> +         case ABS_MT_POSITION_Y:
> +             if (hw->multi_touch_count)
> +                 hw->multi_touch_y = ev.value;
> +             else
> +                 hw->y = ev.value;
>               break;
>           case ABS_PRESSURE:
> -             hw->z = ev.value;
> +         case ABS_MT_PRESSURE:
> +             if (hw->multi_touch_count)
> +                 hw->multi_touch_z = ev.value;
> +             else
> +                 hw->z = ev.value;
>               break;
>           case ABS_TOOL_WIDTH:
>               hw->fingerWidth = ev.value;
> @@ -493,6 +538,7 @@ EventReadDevDimensions(InputInfoPtr pInfo)
>      if (event_query_is_touchpad(pInfo->fd, (need_grab) ? *need_grab : TRUE))
>       event_query_axis_ranges(pInfo);
>      event_query_info(pInfo);
> +    event_query_multi_touch(local);
>      event_query_clickpad(local);
>      event_query_led(local);
>  }
> diff --git a/src/synaptics.c b/src/synaptics.c
> index bd52730..05df1c8 100644
> --- a/src/synaptics.c
> +++ b/src/synaptics.c
> @@ -1198,6 +1198,37 @@ static inline int 
> get_touch_button_area(SynapticsPrivate *priv)
>  
>  #define is_main_bottom_edge(hw, priv) \
>      ((hw)->y >= get_touch_button_area(priv))
> +#define is_multi_touch_bottom_edge(hw, priv) \
> +    ((hw)->multi_touch_y >= get_touch_button_area(priv))
> +
> +static void swap_hw_pts(struct SynapticsHwState *hw);
> +
> +/* if only main ptr is in button area, track another ptr as primary */
> +static void
> +track_clickpad_multi_touch(SynapticsPrivate *priv, struct SynapticsHwState 
> *hw)
> +{
> +    if (hw->multi_touch > 1 && is_main_bottom_edge(hw, priv) &&
> +     !is_multi_touch_bottom_edge(hw, priv) &&
> +     (hw->left || priv->multi_touch_mode < MULTI_TOUCH_MODE_GESTURE)) {
> +     swap_hw_pts(hw);
> +     priv->count_packet_finger = 0; /* to avoid jump */
> +    }
> +}
> +
> +static int
> +clickpad_init_multi_touch_mode(SynapticsPrivate *priv, struct 
> SynapticsHwState *hw)
> +{
> +    if (hw->multi_touch <= 1)
> +     return MULTI_TOUCH_MODE_NONE;
> +    /* both fingers in button-area; likely multi-finger gestures */
> +    if (is_main_bottom_edge(hw, priv))
> +     return MULTI_TOUCH_MODE_START;
> +    /* no fingers in button area; normal multi-touch */
> +    if (!is_multi_touch_bottom_edge(hw, priv))
> +     return MULTI_TOUCH_MODE_START;
> +    /* suppress gestures */
> +    return MULTI_TOUCH_MODE_BUTTON;
> +}
>  
>  static void reset_state_as_moving(SynapticsPrivate *priv, struct 
> SynapticsHwState *hw)
>  {
> @@ -1219,9 +1250,14 @@ handle_clickpad(LocalDevicePtr local, struct 
> SynapticsHwState *hw)
>  {
>      SynapticsPrivate *priv = (SynapticsPrivate *) (local->private);
>      SynapticsParameters *para = &priv->synpara;
> -    int in_main_button;
> +    int in_main_button, in_multi_button;
>  
>      in_main_button = is_main_bottom_edge(hw, priv);
> +    if (hw->multi_touch > 1)
> +     in_multi_button = is_multi_touch_bottom_edge(hw, priv);
> +    else
> +     in_multi_button = 0;
> +
>      if (in_main_button) {
>       if (hw->left) {
>           /* when button is pressed solely, don't move and ignore tapping */
> @@ -1239,27 +1275,34 @@ handle_clickpad(LocalDevicePtr local, struct 
> SynapticsHwState *hw)
>      }
>  
>      if (hw->left) { /* clicked? */
> -     if (priv->prev_hw.left || priv->prev_hw.right || priv->prev_hw.middle) {
> +     if (priv->multi_touch_mode == MULTI_TOUCH_MODE_DRAG) {
>           /* 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) {
> +     } else if (in_main_button || in_multi_button) {
>           /* start dragging */
>           hw->left = 0;
>           if (in_main_button)
>               get_clickpad_button(priv, hw, hw->x);
> +         if (in_multi_button)
> +             get_clickpad_button(priv, hw, hw->multi_touch_x);
> +         priv->multi_touch_mode = MULTI_TOUCH_MODE_DRAG;
>       }
>      } else {
>       /* button being released, reset dragging if necessary */
> -     if (priv->prev_hw.left || priv->prev_hw.right || priv->prev_hw.middle) {
> +     if (priv->multi_touch_mode == MULTI_TOUCH_MODE_DRAG) {
> +         priv->multi_touch_mode = clickpad_init_multi_touch_mode(priv, hw);
>           priv->count_packet_finger = 0;
> -         reset_state_as_moving(priv, hw);
> +         if (priv->multi_touch_mode != MULTI_TOUCH_MODE_START) {
> +             reset_state_as_moving(priv, hw);
> +         }
>       }
>       hw->left = hw->right = hw->middle = 0;
>      }
>  
> -    if (in_main_button && para->touch_button_sticky > 0) {
> +    if (priv->multi_touch_mode == MULTI_TOUCH_MODE_NONE &&
> +     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;
> @@ -1962,7 +2005,8 @@ ComputeDeltas(SynapticsPrivate *priv, const struct 
> SynapticsHwState *hw,
>      if (inside_area && moving_state && !priv->palm &&
>       !priv->vert_scroll_edge_on && !priv->horiz_scroll_edge_on &&
>       !priv->vert_scroll_twofinger_on && !priv->horiz_scroll_twofinger_on &&
> -     !priv->circ_scroll_on && priv->prevFingers == hw->numFingers) {
> +     !priv->circ_scroll_on && priv->prevFingers == hw->numFingers &&
> +     priv->multi_touch_mode < MULTI_TOUCH_MODE_GESTURE) {
>       /* FIXME: Wtf?? what's with 13? */
>       delay = MIN(delay, 13);
>       if (priv->count_packet_finger > 3) { /* min. 3 packets */
> @@ -2130,7 +2174,8 @@ HandleScrolling(SynapticsPrivate *priv, struct 
> SynapticsHwState *hw,
>      }
>      if (!priv->circ_scroll_on) {
>       if (finger) {
> -         if (hw->numFingers == 2) {
> +         if (hw->numFingers == 2 &&
> +             priv->multi_touch_mode <= MULTI_TOUCH_MODE_START) {
>               if (!priv->vert_scroll_twofinger_on &&
>                   (para->scroll_twofinger_vert) && (para->scroll_dist_vert != 
> 0)) {
>                   priv->vert_scroll_twofinger_on = TRUE;
> @@ -2269,10 +2314,14 @@ HandleScrolling(SynapticsPrivate *priv, struct 
> SynapticsHwState *hw,
>           while (hw->y - priv->scroll_y > delta) {
>               sd->down++;
>               priv->scroll_y += delta;
> +             if (priv->multi_touch_mode == MULTI_TOUCH_MODE_START)
> +                 priv->multi_touch_mode = MULTI_TOUCH_MODE_SCROLL;
>           }
>           while (hw->y - priv->scroll_y < -delta) {
>               sd->up++;
>               priv->scroll_y -= delta;
> +             if (priv->multi_touch_mode == MULTI_TOUCH_MODE_START)
> +                 priv->multi_touch_mode = MULTI_TOUCH_MODE_SCROLL;
>           }
>       }
>      }
> @@ -2283,10 +2332,14 @@ HandleScrolling(SynapticsPrivate *priv, struct 
> SynapticsHwState *hw,
>           while (hw->x - priv->scroll_x > delta) {
>               sd->right++;
>               priv->scroll_x += delta;
> +             if (priv->multi_touch_mode == MULTI_TOUCH_MODE_START)
> +                 priv->multi_touch_mode = MULTI_TOUCH_MODE_SCROLL;
>           }
>           while (hw->x - priv->scroll_x < -delta) {
>               sd->left++;
>               priv->scroll_x -= delta;
> +             if (priv->multi_touch_mode == MULTI_TOUCH_MODE_START)
> +                 priv->multi_touch_mode = MULTI_TOUCH_MODE_SCROLL;
>           }
>       }
>      }
> @@ -2477,7 +2530,8 @@ update_hw_button_state(const InputInfoPtr pInfo, struct 
> SynapticsHwState *hw, in
>      hw->middle |= HandleMidButtonEmulation(priv, hw, delay);
>  
>      /* Fingers emulate other buttons */
> -    if(hw->left && hw->numFingers >= 1){
> +    if(hw->left && hw->numFingers >= 1 &&
> +       priv->multi_touch_mode < MULTI_TOUCH_MODE_BUTTON) {
>          handle_clickfinger(para, hw);
>      }
>  
> @@ -2486,6 +2540,65 @@ update_hw_button_state(const InputInfoPtr pInfo, 
> struct SynapticsHwState *hw, in
>          hw->fingerWidth >= para->emulate_twofinger_w) {
>       hw->numFingers = 2;
>      }
> +
> +    /* don't handle as multi-touching if a finger is in the click zone */
> +    if (hw->numFingers > 1 &&
> +     (priv->multi_touch_mode == MULTI_TOUCH_MODE_BUTTON ||
> +      priv->multi_touch_mode == MULTI_TOUCH_MODE_DRAG)) {
> +     hw->numFingers = 1;
> +     priv->vert_scroll_twofinger_on = FALSE;
> +     priv->horiz_scroll_twofinger_on = FALSE;
> +   }
> +}
> +
> +#define SWAP(a, b) do { int _tmp = (a); (a) = (b); (b) = _tmp; } while (0)
> +static void swap_hw_pts(struct SynapticsHwState *hw)
> +{
> +    SWAP(hw->x, hw->multi_touch_x);
> +    SWAP(hw->y, hw->multi_touch_y);
> +    SWAP(hw->z, hw->multi_touch_z);
> +}
> +
> +static inline int square_distance(int x0, int y0, int x1, int y1)
> +{
> +    return SQR(x0 - x1) + SQR(y0 - y1);
> +}
> +
> +static void
> +update_multi_touch(SynapticsPrivate *priv, struct SynapticsHwState *hw)
> +{
> +    if (hw->multi_touch > 1) {
> +     /* track the new multi-touch */
> +     if (square_distance(priv->prev_hw.x, priv->prev_hw.y, hw->x, hw->y) >
> +         square_distance(priv->prev_hw.x, priv->prev_hw.y,
> +                         hw->multi_touch_x, hw->multi_touch_y))
> +         swap_hw_pts(hw);
> +     if (priv->is_clickpad)
> +         track_clickpad_multi_touch(priv, hw);
> +     if (priv->multi_touch_mode == MULTI_TOUCH_MODE_NONE) {
> +         if (priv->is_clickpad)
> +             priv->multi_touch_mode = clickpad_init_multi_touch_mode(priv, 
> hw);
> +         else
> +             priv->multi_touch_mode = MULTI_TOUCH_MODE_START;
> +     }
> +    } else {
> +     if (priv->multi_touch_mode != MULTI_TOUCH_MODE_NONE) {
> +         if (priv->multi_touch_mode == MULTI_TOUCH_MODE_DRAG)
> +             reset_state_as_moving(priv, hw);
> +         /* Reset some states to avoid the unexpected jump after
> +          * releasing the multi-touch finger.
> +          * This is rather hackish, so a cleaner way is needed...
> +          */
> +         if (priv->finger_state && hw->z) {
> +             priv->touch_on.x = hw->x;
> +             priv->touch_on.x = hw->y;
> +         }
> +         priv->count_packet_finger = 0;
> +         priv->multi_touch_mode = MULTI_TOUCH_MODE_NONE;
> +         priv->vert_scroll_twofinger_on = FALSE;
> +         priv->horiz_scroll_twofinger_on = FALSE;
> +     }
> +    }
>  }
>  
>  static void
> @@ -2581,6 +2694,8 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState 
> *hw)
>      int timeleft;
>      Bool inside_active_area;
>  
> +    update_multi_touch(priv, hw);
> +
>      update_shm(pInfo, hw);
>  
>      /* If touchpad is switched off, we skip the whole thing and return delay 
> */
> @@ -2661,6 +2776,9 @@ HandleState(InputInfoPtr pInfo, struct SynapticsHwState 
> *hw)
>      }
>      delay = MIN(delay, timeleft);
>  
> +    /* don't move pointer while multi-touching (except for clickpad 
> dragging) */
> +    if (priv->multi_touch_mode != MULTI_TOUCH_MODE_NONE && hw->numFingers > 
> 1)
> +     dx = dy = 0;
>  
>      buttons = ((hw->left     ? 0x01 : 0) |
>              (hw->middle   ? 0x02 : 0) |
> diff --git a/src/synapticsstr.h b/src/synapticsstr.h
> index 44925e5..34038ae 100644
> --- a/src/synapticsstr.h
> +++ b/src/synapticsstr.h
> @@ -168,6 +168,15 @@ typedef struct _SynapticsParameters
>  } SynapticsParameters;
>  
>  
> +enum MultiTouchMode {
> +    MULTI_TOUCH_MODE_NONE,
> +    MULTI_TOUCH_MODE_START,
> +    MULTI_TOUCH_MODE_BUTTON,
> +    MULTI_TOUCH_MODE_DRAG,
> +    MULTI_TOUCH_MODE_SCROLL,
> +    MULTI_TOUCH_MODE_GESTURE = MULTI_TOUCH_MODE_SCROLL,
> +};
> +
>  typedef struct _SynapticsPrivateRec
>  {
>      SynapticsParameters synpara;            /* Default parameter settings, 
> read from
> @@ -241,6 +250,7 @@ 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 can_multi_touch;            /* support multi-touch */
>      Bool ignore_tapping;
>      unsigned int clickpad_threshold;
>      int clickpad_dx, clickpad_dy;
> @@ -252,6 +262,8 @@ typedef struct _SynapticsPrivateRec
>      int led_touch_millis;
>      int led_tap_millis;
>  
> +    enum MultiTouchMode multi_touch_mode;
> +
>      enum TouchpadModel model;          /* The detected model */
>  } SynapticsPrivate;
>  
> diff --git a/src/synproto.h b/src/synproto.h
> index eee56e2..a430699 100644
> --- a/src/synproto.h
> +++ b/src/synproto.h
> @@ -50,6 +50,12 @@ struct SynapticsHwState {
>  
>      Bool multi[8];
>      Bool middle;             /* Some ALPS touchpads have a middle button */
> +
> +    int multi_touch;
> +    int multi_touch_count;
> +    int multi_touch_x;
> +    int multi_touch_y;
> +    int multi_touch_z;
>  };
>  
>  struct CommData {
> -- 
> 1.7.3.1
 
_______________________________________________
[email protected]: X.Org development
Archives: http://lists.x.org/archives/xorg-devel
Info: http://lists.x.org/mailman/listinfo/xorg-devel

Reply via email to