Gesture Layer: fix to improve fingers taps registration
authorAharon Hillel <a.hillel@samsung.com>
Sun, 9 Jun 2013 13:14:08 +0000 (16:14 +0300)
committerSungho Kwak <sungho1.kwak@samsung.com>
Thu, 27 Jun 2013 05:53:06 +0000 (14:53 +0900)
Signed-off-by: Aharon Hillel <a.hillel@samsung.com>
src/lib/elm_gesture_layer.c

index fdca6ab..b496548 100644 (file)
@@ -1574,11 +1574,6 @@ _pe_device_compare(const void *data1,
    const Pointer_Event *pe1 = eina_list_data_get(data1);
    const Pointer_Event *pe2 = data2;
 
-   /* Only match if last was a down event */
-   if ((pe1->event_type != EVAS_CALLBACK_MULTI_DOWN) &&
-       (pe1->event_type != EVAS_CALLBACK_MOUSE_DOWN))
-     return 1;
-
    if (pe1->device == pe2->device)
      return 0;
    else if (pe1->device < pe2->device)
@@ -1624,6 +1619,51 @@ _pointer_event_record(Taps_Type *st,
 /**
  * @internal
  *
+ * This function computes minimum rect to bound taps at idx index
+ *
+ * @param taps [in] List of lists containing taps info.
+ * @param idx [in] index of events taken from lists.
+ * @param r [out] rect object to save info
+ * @return EINA_TRUE if managed to compute rect.
+ *
+ * @ingroup Elm_Gesture_Layer
+ */
+static Eina_Bool
+_taps_rect_get(Eina_List *taps, int idx, Evas_Coord_Rectangle *r)
+{  /* Build a rect bounding all taps at index idx */
+   Eina_List *l;
+   Evas_Coord bx, by;
+   Eina_List *pe_list;
+   Eina_Bool was_init = EINA_FALSE;
+
+   EINA_LIST_FOREACH(taps, l, pe_list)
+     {
+        Pointer_Event *pe = eina_list_nth(pe_list, idx);
+        if (!pe) continue;  /* Not suppose to happen */
+
+        if (was_init)
+          {
+             if (pe->x < r->x) r->x = pe->x;
+             if (pe->y < r->y) r->y = pe->y;
+             if (pe->x > bx) bx = pe->x;
+             if (pe->y > by) by = pe->y;
+          }
+        else
+          {
+             r->x = bx = pe->x;
+             r->y = by = pe->y;
+             was_init = EINA_TRUE;
+          }
+     }
+
+   r->w = bx - r->x;
+   r->h = by - r->y;
+   return was_init;
+}
+
+/**
+ * @internal
+ *
  * This function checks if the tap gesture is done.
  *
  * @param data gesture info pointer
@@ -1637,9 +1677,13 @@ _tap_gesture_check_finish(Gesture_Info *gesture)
    /* Here we check if taps-gesture was completed successfuly */
    /* Count how many taps were recieved on each device then   */
    /* determine if it matches n_taps_needed defined on START  */
+   unsigned int i;
    Taps_Type *st = gesture->data;
    Eina_List *l;
    Eina_List *pe_list;
+   Evas_Coord_Rectangle base;
+   Evas_Coord_Rectangle tmp;
+   Evas_Coord tap_finger_size = elm_config_finger_size_get();
 
    if (!st->l) return EINA_FALSE;
    EINA_LIST_FOREACH(st->l, l, pe_list)
@@ -1651,6 +1695,29 @@ _tap_gesture_check_finish(Gesture_Info *gesture)
           }
      }
 
+   /* Now bound each tap touches in a rect, compare diff within tolerance */
+   /* Get rect based on first DOWN events for all devices */
+   if (!_taps_rect_get(st->l, 0, &base))
+     return EINA_FALSE;  /* Should not happen */
+
+   for(i = 1; i < st->n_taps_needed; i++)
+     {  /* Compare all other rects to base, tolerance is finger size */
+        if (_taps_rect_get(st->l, i, &tmp))
+          {
+             if (abs(tmp.x - base.x) > tap_finger_size)
+               return EINA_FALSE;
+
+             if (abs(tmp.y - base.y) > tap_finger_size)
+               return EINA_FALSE;
+
+             if (abs((tmp.x + tmp.w) - (base.x + base.w)) > tap_finger_size)
+               return EINA_FALSE;
+
+             if (abs((tmp.y + tmp.h) - (base.y + base.h)) > tap_finger_size)
+               return EINA_FALSE;
+          }
+     }
+
    return EINA_TRUE;
 }
 
@@ -1757,7 +1824,7 @@ _tap_gesture_test(Evas_Object *obj,
    Taps_Type *st;
    Gesture_Info *gesture;
    Eina_List *pe_list = NULL;
-   Pointer_Event *pe_down = NULL;
+   Pointer_Event *pe_last = NULL;
    Evas_Event_Flags ev_flag = EVAS_EVENT_FLAG_NONE;
 
    /* Here we fill Tap struct */
@@ -1772,20 +1839,20 @@ _tap_gesture_test(Evas_Object *obj,
    switch (g_type)
      {
       case ELM_GESTURE_N_TAPS:
-        taps = 1;
-        break;
+         taps = 1;
+         break;
 
       case ELM_GESTURE_N_DOUBLE_TAPS:
-        taps = 2;
-        break;
+         taps = 2;
+         break;
 
       case ELM_GESTURE_N_TRIPLE_TAPS:
-        taps = 3;
-        break;
+         taps = 3;
+         break;
 
       default:
-        taps = 0;
-        break;
+         taps = 0;
+         break;
      }
 
    st = gesture->data;
@@ -1800,94 +1867,105 @@ _tap_gesture_test(Evas_Object *obj,
      {
       case EVAS_CALLBACK_MULTI_DOWN:
       case EVAS_CALLBACK_MOUSE_DOWN:
-        /* Check if got tap on same cord was tapped before */
-        pe_list = eina_list_search_unsorted(st->l, _match_fingers_compare, pe);
-
-        if ((!pe_list) &&
-            /* This device was touched in other cord before completion */
-            eina_list_search_unsorted(st->l, _pe_device_compare, pe))
-          {
-             ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
-                                  &st->info, EINA_FALSE);
-             _event_consume(sd, event_info, event_type, ev_flag);
+         /* Each device taps (DOWN, UP event) registered in same list    */
+         /* Find list for this device or start a new list if not found   */
+         pe_list = eina_list_search_unsorted(st->l, _pe_device_compare, pe);
+         if (pe_list)
+           {  /* This device touched before, verify that this tap is on  */
+              /* top of a previous tap (including a tap of other device) */
+              if (!eina_list_search_unsorted(st->l, _match_fingers_compare, pe))
+                {  /* New DOWN event is not on top of any prev touch     */
+                   ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
+                         &st->info, EINA_FALSE);
+                   _event_consume(sd, event_info, event_type, ev_flag);
 
-             return;
-          }
+                   return;
+                }
+           }
 
-        pe_list = _pointer_event_record
+         /* All tests are good, register this tap in device list */
+         pe_list = _pointer_event_record
             (st, pe_list, pe, sd, event_info, event_type);
-        if (!sd->gest_taps_timeout)
-          {
-             if (sd->double_tap_timeout > 0.0)
-               {
-                  sd->gest_taps_timeout =
-                     ecore_timer_add(sd->double_tap_timeout,
-                           _multi_tap_timeout, gesture->obj);
-               }
-          }
-        else
-          ecore_timer_reset(sd->gest_taps_timeout);
 
-        /* This is the first mouse down we got */
-        if ((pe->device == 0) && (eina_list_count(pe_list) == 1))
-          {
-             ev_flag = _state_set(gesture, ELM_GESTURE_STATE_START,
-                                  &st->info, EINA_FALSE);
-             _event_consume(sd, event_info, event_type, ev_flag);
+         if (!sd->gest_taps_timeout)
+           {
+              if (sd->double_tap_timeout > 0.0)
+                {
+                   sd->gest_taps_timeout =
+                      ecore_timer_add(sd->double_tap_timeout,
+                            _multi_tap_timeout, gesture->obj);
+                }
+           }
+         else  /* We re-allocate gest_taps_timeout between taps */
+           ecore_timer_reset(sd->gest_taps_timeout);
 
-             st->n_taps_needed = taps * 2;  /* count DOWN and UP */
+         if ((pe->device == 0) && (eina_list_count(pe_list) == 1))
+           {  /* This is the first mouse down we got */
+              ev_flag = _state_set(gesture, ELM_GESTURE_STATE_START,
+                    &st->info, EINA_FALSE);
+              _event_consume(sd, event_info, event_type, ev_flag);
 
-             return;
-          }
-        else if (eina_list_count(pe_list) > st->n_taps_needed)
-          /* If we arleady got too many touches for this gesture. */
-          ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
-                               &st->info, EINA_FALSE);
+              st->n_taps_needed = taps * 2;  /* count DOWN and UP */
 
-        break;
+              return;
+           }
+         else if (eina_list_count(pe_list) > st->n_taps_needed)
+           {  /* If we arleady got too many touches for this gesture. */
+              ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
+                    &st->info, EINA_FALSE);
+           }
+
+         break;
 
       case EVAS_CALLBACK_MULTI_UP:
       case EVAS_CALLBACK_MOUSE_UP:
-        pe_list = eina_list_search_unsorted(st->l, _pe_device_compare, pe);
-        if (!pe_list) return;
+         pe_list = eina_list_search_unsorted(st->l, _pe_device_compare, pe);
+         if (!pe_list) return;
 
-        pe_list = _pointer_event_record
+         pe_list = _pointer_event_record
             (st, pe_list, pe, sd, event_info, event_type);
 
-        if (((gesture->g_type == ELM_GESTURE_N_TAPS) &&
-             !IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS) &&
-             !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)) ||
-            ((gesture->g_type == ELM_GESTURE_N_DOUBLE_TAPS) &&
-             !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)))
-          {
-             if (_tap_gesture_check_finish(gesture))
-               {
-                  _tap_gesture_finish(gesture);
-                  return;
-               }
-          }
+         if (((gesture->g_type == ELM_GESTURE_N_TAPS) &&
+                  !IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS) &&
+                  !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)) ||
+               ((gesture->g_type == ELM_GESTURE_N_DOUBLE_TAPS) &&
+                !IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS)))
+           {  /* Test for finish immidiatly, not waiting for timeout */
+              if (_tap_gesture_check_finish(gesture))
+                {
+                   _tap_gesture_finish(gesture);
+                   return;
+                }
+           }
 
-        break;
+         break;
 
       case EVAS_CALLBACK_MULTI_MOVE:
       case EVAS_CALLBACK_MOUSE_MOVE:
-        /* Get first event in first list, this has to be a Mouse Down event  */
-        /* and verify that user didn't move out of this area before next tap */
-        pe_list = eina_list_search_unsorted(st->l, _pe_device_compare, pe);
-        if (pe_list)
-          {
-             pe_down = eina_list_data_get(pe_list);
-             if (!_inside(pe_down->x, pe_down->y, pe->x, pe->y))
-               {
-                  ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
-                                       &st->info, EINA_FALSE);
-                  _event_consume(sd, event_info, event_type, ev_flag);
-               }
-          }
-        break;
+         /* Verify that user didn't move out of tap area before next tap */
+         /* BUT: we need to skip some MOVE events coming before DOWN     */
+         /* when user taps next tap. So fetch LAST recorded event for    */
+         /* device (DOWN or UP event), ignore all MOVEs if last was UP   */
+         pe_last = eina_list_data_get(eina_list_last(
+                  eina_list_search_unsorted(st->l, _pe_device_compare, pe)));
+
+         if (pe_last)
+           {  /* pe_last is the last event recorded for this device */
+              if ((pe_last->event_type == EVAS_CALLBACK_MOUSE_DOWN) ||
+                    (pe_last->event_type == EVAS_CALLBACK_MULTI_DOWN))
+                {  /* Test only MOVE events that come after DOWN event */
+                   if (!_inside(pe_last->x, pe_last->y, pe->x, pe->y))
+                     {
+                        ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
+                              &st->info, EINA_FALSE);
+                        _event_consume(sd, event_info, event_type, ev_flag);
+                     }
+                }
+           }
+         break;
 
       default:
-        return;
+         return;
      }
 }