elm_gesture_layer: Add new config value to handle tap finger size.
[platform/upstream/elementary.git] / src / lib / elm_gesture_layer.c
index 6d36461..e50e0d4 100644 (file)
@@ -1,11 +1,14 @@
+#ifdef HAVE_CONFIG_H
+# include "elementary_config.h"
+#endif
+
 #include <Elementary.h>
 #include "elm_priv.h"
 
-EAPI Eo_Op ELM_OBJ_GESTURE_LAYER_BASE_ID = EO_NOOP;
-
-#define MY_CLASS ELM_OBJ_GESTURE_LAYER_CLASS
+#define MY_CLASS ELM_GESTURE_LAYER_CLASS
 
-#define MY_CLASS_NAME "elm_gesture_layer"
+#define MY_CLASS_NAME "Elm_Gesture_Layer"
+#define MY_CLASS_NAME_LEGACY "elm_gesture_layer"
 
 /* Some defaults */
 #define ELM_MOUSE_DEVICE             0
@@ -40,10 +43,10 @@ _glayer_buf_dup(void *buf, size_t size)
 
 #define SET_TEST_BIT(P)                               \
   do {                                                \
-       P->test = P->fn[ELM_GESTURE_STATE_START].cb || \
-         P->fn[ELM_GESTURE_STATE_MOVE].cb ||          \
-         P->fn[ELM_GESTURE_STATE_END].cb ||           \
-         P->fn[ELM_GESTURE_STATE_ABORT].cb;           \
+       P->test = P->cbs[ELM_GESTURE_STATE_START] || \
+         P->cbs[ELM_GESTURE_STATE_MOVE] ||          \
+         P->cbs[ELM_GESTURE_STATE_END] ||           \
+         P->cbs[ELM_GESTURE_STATE_ABORT];           \
     } while (0)
 
 #define IS_TESTED_GESTURE(gesture) \
@@ -53,13 +56,13 @@ _glayer_buf_dup(void *buf, size_t size)
   ((sd->gesture[T]) ? sd->gesture[T]->test : EINA_FALSE)
 
 #define ELM_GESTURE_LAYER_DATA_GET(o, sd) \
-  Elm_Gesture_Layer_Smart_Data * sd = eo_data_get(o, MY_CLASS)
+  Elm_Gesture_Layer_Data * sd = eo_data_scope_get(o, MY_CLASS)
 
 #define ELM_GESTURE_LAYER_DATA_GET_OR_RETURN(o, ptr) \
   ELM_GESTURE_LAYER_DATA_GET(o, ptr);                \
   if (!ptr)                                          \
     {                                                \
-       CRITICAL("No widget data for object %p (%s)", \
+       CRI("No widget data for object %p (%s)", \
                 o, evas_object_type_get(o));         \
        return;                                       \
     }
@@ -68,7 +71,7 @@ _glayer_buf_dup(void *buf, size_t size)
   ELM_GESTURE_LAYER_DATA_GET(o, ptr);                         \
   if (!ptr)                                                   \
     {                                                         \
-       CRITICAL("No widget data for object %p (%s)",          \
+       CRI("No widget data for object %p (%s)",          \
                 o, evas_object_type_get(o));                  \
        return val;                                            \
     }
@@ -114,6 +117,7 @@ typedef struct _Pointer_Event Pointer_Event;
  */
 struct _Func_Data
 {
+   EINA_INLIST;
    void                *user_data; /**< Holds user data to CB (like sd) */
    Elm_Gesture_Event_Cb cb;
 };
@@ -140,7 +144,7 @@ struct _Gesture_Info
 {
    Evas_Object      *obj;
    void             *data; /**< Holds gesture intemidiate processing data */
-   Func_Data         fn[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info for states */
+   Eina_Inlist      *cbs[ELM_GESTURE_STATE_ABORT + 1]; /**< Callback info (Func_Data) for states */
    Elm_Gesture_Type  g_type; /**< gesture type */
    Elm_Gesture_State state; /**< gesture state */
    void             *info; /**< Data for the state callback */
@@ -209,6 +213,8 @@ static void _event_process(void *data,
                            void *event_info,
                            Evas_Callback_Type event_type);
 
+static void _callbacks_unregister(Evas_Object *obj);
+
 /* Should be the same order as _Elm_Gesture_Type */
 static Tests_Array_Funcs _glayer_tests_array[] = {
    { NULL, NULL, NULL },     /** Because someone made an awful mistake. */
@@ -351,8 +357,8 @@ struct _Rotate_Type /* Fields used by _rotation_test() */
 };
 typedef struct _Rotate_Type                  Rotate_Type;
 
-typedef struct _Elm_Gesture_Layer_Smart_Data Elm_Gesture_Layer_Smart_Data;
-struct _Elm_Gesture_Layer_Smart_Data
+typedef struct _Elm_Gesture_Layer_Data Elm_Gesture_Layer_Data;
+struct _Elm_Gesture_Layer_Data
 {
    Evas_Object          *target; /* Target Widget */
    Event_History        *event_history_list;
@@ -378,6 +384,7 @@ struct _Elm_Gesture_Layer_Smart_Data
    Eina_List            *touched; /* Information  of touched devices */
 
    /* Taps Gestures */
+   Evas_Coord           tap_finger_size;    /* Default from Config */
    Ecore_Timer          *gest_taps_timeout; /* When this expires, dbl
                                              * click/taps ABORTed  */
 
@@ -527,7 +534,7 @@ _event_flag_get(void *event_info,
  * @ingroup Elm_Gesture_Layer
  */
 static void
-_event_consume(Elm_Gesture_Layer_Smart_Data *sd,
+_event_consume(Elm_Gesture_Layer_Data *sd,
                void *event_info,
                Evas_Callback_Type event_type,
                Evas_Event_Flags ev_flags)
@@ -599,14 +606,16 @@ static Evas_Event_Flags
 _state_report(Gesture_Info *gesture,
               void *info)
 {
+   Evas_Event_Flags flags = EVAS_EVENT_FLAG_NONE;
    /* We report current state (START, MOVE, END, ABORT), once */
    if ((gesture->state != ELM_GESTURE_STATE_UNDEFINED) &&
-       (gesture->fn[gesture->state].cb)) /* Fill state-info struct and
-                                          * send ptr to user
-                                          * callback */
+       (gesture->cbs[gesture->state])) /* Fill state-info struct and
+                                        * send ptr to user
+                                        * callback */
      {
-        return gesture->fn[gesture->state].cb(
-                 gesture->fn[gesture->state].user_data, info);
+        Func_Data *cb_info;
+        EINA_INLIST_FOREACH(gesture->cbs[gesture->state], cb_info)
+           flags |= cb_info->cb(cb_info->user_data, info);
      }
 
    return EVAS_EVENT_FLAG_NONE;
@@ -672,7 +681,7 @@ _state_set(Gesture_Info *g,
  * @ingroup Elm_Gesture_Layer
  */
 static void
-_states_reset(Elm_Gesture_Layer_Smart_Data *sd)
+_states_reset(Elm_Gesture_Layer_Data *sd)
 {
    int i;
    Gesture_Info *p;
@@ -702,7 +711,7 @@ _states_reset(Elm_Gesture_Layer_Smart_Data *sd)
  * @ingroup Elm_Gesture_Layer
  */
 static Eina_Bool
-_pointer_event_make(void *data __UNUSED__,
+_pointer_event_make(void *data EINA_UNUSED,
                     void *event_info,
                     Evas_Callback_Type event_type,
                     Pointer_Event *pe)
@@ -829,6 +838,8 @@ _event_history_add(Evas_Object *obj,
    ELM_GESTURE_LAYER_DATA_GET(obj, sd);
 
    ev = malloc(sizeof(Event_History));
+   if (!ev) return EINA_FALSE;
+
    ev->event = _event_info_copy(event, event_type);  /* Freed on
                                                       * _event_history_clear */
    ev->event_type = event_type;
@@ -849,8 +860,8 @@ _event_history_add(Evas_Object *obj,
  */
 static void
 _mouse_down_cb(void *data,
-               Evas *e __UNUSED__,
-               Evas_Object *obj __UNUSED__,
+               Evas *e EINA_UNUSED,
+               Evas_Object *obj EINA_UNUSED,
                void *event_info)
 {
    if (((Evas_Event_Mouse_Down *)event_info)->button != 1)
@@ -861,8 +872,8 @@ _mouse_down_cb(void *data,
 
 static void
 _mouse_move_cb(void *data,
-               Evas *e __UNUSED__,
-               Evas_Object *obj __UNUSED__,
+               Evas *e EINA_UNUSED,
+               Evas_Object *obj EINA_UNUSED,
                void *event_info)
 {
    _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_MOVE);
@@ -870,8 +881,8 @@ _mouse_move_cb(void *data,
 
 static void
 _key_down_cb(void *data,
-             Evas *e __UNUSED__,
-             Evas_Object *obj __UNUSED__,
+             Evas *e EINA_UNUSED,
+             Evas_Object *obj EINA_UNUSED,
              void *event_info)
 {
    _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_DOWN);
@@ -879,8 +890,8 @@ _key_down_cb(void *data,
 
 static void
 _key_up_cb(void *data,
-           Evas *e __UNUSED__,
-           Evas_Object *obj __UNUSED__,
+           Evas *e EINA_UNUSED,
+           Evas_Object *obj EINA_UNUSED,
            void *event_info)
 {
    _event_process(data, obj, event_info, EVAS_CALLBACK_KEY_UP);
@@ -888,8 +899,8 @@ _key_up_cb(void *data,
 
 static void
 _mouse_up_cb(void *data,
-             Evas *e __UNUSED__,
-             Evas_Object *obj __UNUSED__,
+             Evas *e EINA_UNUSED,
+             Evas_Object *obj EINA_UNUSED,
              void *event_info)
 {
    if (((Evas_Event_Mouse_Up *)event_info)->button != 1)
@@ -900,8 +911,8 @@ _mouse_up_cb(void *data,
 
 static void
 _mouse_wheel_cb(void *data,
-                Evas *e __UNUSED__,
-                Evas_Object *obj __UNUSED__,
+                Evas *e EINA_UNUSED,
+                Evas_Object *obj EINA_UNUSED,
                 void *event_info)
 {
    _event_process(data, obj, event_info, EVAS_CALLBACK_MOUSE_WHEEL);
@@ -909,8 +920,8 @@ _mouse_wheel_cb(void *data,
 
 static void
 _multi_down_cb(void *data,
-               Evas *e __UNUSED__,
-               Evas_Object *obj __UNUSED__,
+               Evas *e EINA_UNUSED,
+               Evas_Object *obj EINA_UNUSED,
                void *event_info)
 {
    /* Skip the mouse duplicates. */
@@ -921,8 +932,8 @@ _multi_down_cb(void *data,
 
 static void
 _multi_move_cb(void *data,
-               Evas *e __UNUSED__,
-               Evas_Object *obj __UNUSED__,
+               Evas *e EINA_UNUSED,
+               Evas_Object *obj EINA_UNUSED,
                void *event_info)
 {
    /* Skip the mouse duplicates. */
@@ -933,8 +944,8 @@ _multi_move_cb(void *data,
 
 static void
 _multi_up_cb(void *data,
-             Evas *e __UNUSED__,
-             Evas_Object *obj __UNUSED__,
+             Evas *e EINA_UNUSED,
+             Evas_Object *obj EINA_UNUSED,
              void *event_info)
 {
    /* Skip the mouse duplicates. */
@@ -943,6 +954,17 @@ _multi_up_cb(void *data,
    _event_process(data, obj, event_info, EVAS_CALLBACK_MULTI_UP);
 }
 
+static void
+_target_del_cb(void *data,
+               Evas *e EINA_UNUSED,
+               Evas_Object *obj EINA_UNUSED,
+               void *event_info EINA_UNUSED)
+{
+   _callbacks_unregister(data);
+   ELM_GESTURE_LAYER_DATA_GET(data, sd);
+   sd->target = NULL;
+}
+
 /**
  * @internal
  *
@@ -981,6 +1003,9 @@ _callbacks_register(Evas_Object *obj)
      (sd->target, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, obj);
    evas_object_event_callback_add
      (sd->target, EVAS_CALLBACK_KEY_UP, _key_up_cb, obj);
+
+   evas_object_event_callback_add
+     (sd->target, EVAS_CALLBACK_DEL, _target_del_cb, obj);
 }
 
 /**
@@ -1022,6 +1047,9 @@ _callbacks_unregister(Evas_Object *obj)
      (sd->target, EVAS_CALLBACK_KEY_DOWN, _key_down_cb, obj);
    evas_object_event_callback_del_full
      (sd->target, EVAS_CALLBACK_KEY_UP, _key_up_cb, obj);
+
+   evas_object_event_callback_del_full
+     (sd->target, EVAS_CALLBACK_DEL, _target_del_cb, obj);
 }
 
 /**
@@ -1140,8 +1168,9 @@ _event_history_clear(Evas_Object *obj)
                }
              else
                {  /* Report ABORT to all gestures that still not finished */
-                 _state_set(p, ELM_GESTURE_STATE_ABORT, sd->gesture[i]->info,
-                            EINA_FALSE);
+                  if (sd->target)
+                    _state_set(p, ELM_GESTURE_STATE_ABORT,
+                          sd->gesture[i]->info, EINA_FALSE);
                }
           }
      }
@@ -1309,7 +1338,7 @@ _continues_gestures_restart(void *data,
  */
 static void
 _event_process(void *data,
-               Evas_Object *obj __UNUSED__,
+               Evas_Object *obj EINA_UNUSED,
                void *event_info,
                Evas_Callback_Type event_type)
 {
@@ -1362,9 +1391,12 @@ static Eina_Bool
 _inside(Evas_Coord xx1,
         Evas_Coord yy1,
         Evas_Coord xx2,
-        Evas_Coord yy2)
+        Evas_Coord yy2,
+        Evas_Coord w)
 {
-   int w = elm_config_finger_size_get() >> 1; /* Finger size devided by 2 */
+   w >>= 1; /* Use half the distance, from center to all directions */
+   if (!w)  /* use system default instead */
+     w = elm_config_finger_size_get() >> 1; /* Finger size divided by 2 */
 
    if (xx1 < (xx2 - w))
      return EINA_FALSE;
@@ -1394,11 +1426,7 @@ _tap_gestures_test_reset(Gesture_Info *gesture)
    EINA_SAFETY_ON_NULL_RETURN(gesture);
    ELM_GESTURE_LAYER_DATA_GET(gesture->obj, sd);
 
-   if (sd->gest_taps_timeout)
-     {
-        ecore_timer_del(sd->gest_taps_timeout);
-        sd->gest_taps_timeout = NULL;
-     }
+   ELM_SAFE_FREE(sd->gest_taps_timeout, ecore_timer_del);
 
    if (!gesture->data)
      return;
@@ -1417,7 +1445,6 @@ _tap_gestures_test_reset(Gesture_Info *gesture)
 static void
 _n_long_tap_test_reset(Gesture_Info *gesture)
 {
-   Eina_List *l;
    Pointer_Event *p;
    Long_Tap_Type *st;
 
@@ -1426,15 +1453,11 @@ _n_long_tap_test_reset(Gesture_Info *gesture)
 
    st = gesture->data;
 
-   EINA_LIST_FOREACH(st->touched, l, p)
+   EINA_LIST_FREE(st->touched, p)
      free(p);
+   st->touched = NULL;
 
-   eina_list_free(st->touched);
-   if (st->timeout)
-     {
-        ecore_timer_del(st->timeout);
-        st->timeout = NULL;
-     }
+   ELM_SAFE_FREE(st->timeout, ecore_timer_del);
    memset(gesture->data, 0, sizeof(Long_Tap_Type));
 }
 
@@ -1460,21 +1483,16 @@ _line_data_reset(Line_Data *st)
 static void
 _line_test_reset(Gesture_Info *gesture)
 {
-   Eina_List *l;
    Line_Type *st;
-   Eina_List *list;
    Line_Data *t_line;
 
    EINA_SAFETY_ON_NULL_RETURN(gesture);
    if (!gesture->data) return;
 
    st = gesture->data;
-   list = st->list;
 
-   EINA_LIST_FOREACH(list, l, t_line)
+   EINA_LIST_FREE(st->list, t_line)
      free(t_line);
-
-   eina_list_free(list);
    st->list = NULL;
 }
 
@@ -1515,25 +1533,24 @@ _rotate_test_reset(Gesture_Info *gesture)
    st->rotate_angular_tolerance = sd->rotate_angular_tolerance;
 }
 
-static int
-_match_fingers_compare(const void *data1,
-                       const void *data2)
+static Eina_List *
+_match_fingers_compare(Eina_List *list,
+                       Pointer_Event *pe1,
+                       Evas_Coord w)
 {
-   /* Compare coords of first item in list to cur coords */
-   const Pointer_Event *pe1 = eina_list_data_get(data1);
-   const Pointer_Event *pe2 = data2;
+    /* Compare coords of first item in list to cur coords */
+   Eina_List *pe_list;
+   Eina_List *l;
 
-   if (_inside(pe1->x, pe1->y, pe2->x, pe2->y))
-     return 0;
-   else if (pe1->x < pe2->x)
-     return -1;
-   else
+   EINA_LIST_FOREACH(list, l, pe_list)
      {
-        if (pe1->x == pe2->x)
-          return pe1->y - pe2->y;
-        else
-          return 1;
+        Pointer_Event *pe2 = eina_list_data_get(pe_list);
+
+        if (_inside(pe1->x, pe1->y, pe2->x, pe2->y, w))
+          return pe_list;
      }
+
+  return NULL;
 }
 
 static int
@@ -1544,11 +1561,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)
@@ -1561,7 +1573,7 @@ static Eina_List *
 _pointer_event_record(Taps_Type *st,
                       Eina_List *pe_list,
                       Pointer_Event *pe,
-                      Elm_Gesture_Layer_Smart_Data *sd,
+                      Elm_Gesture_Layer_Data *sd,
                       void *event_info,
                       Evas_Callback_Type event_type)
 {
@@ -1594,6 +1606,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 = 0, by = 0;
+   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
@@ -1602,14 +1659,22 @@ _pointer_event_record(Taps_Type *st,
  * @ingroup Elm_Gesture_Layer
  */
 static Eina_Bool
-_tap_gesture_check_finish(Gesture_Info *gesture)
+_tap_gesture_check_finish(Gesture_Info *gesture, Evas_Coord tap_finger_size)
 {
    /* Here we check if taps-gesture was completed successfuly */
-   /* Count how many taps were recieved on each device then   */
+   /* Count how many taps were received 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;
+   if (!tap_finger_size)  /* Use system default if not set by user */
+     //TIZEN_ONLY(20180321): gesture layer needs their own tap area size to handle sensitivity.
+     //tap_finger_size = elm_config_finger_size_get();
+     tap_finger_size = _elm_config->glayer_tap_finger_size;
+     //
 
    if (!st->l) return EINA_FALSE;
    EINA_LIST_FOREACH(st->l, l, pe_list)
@@ -1621,6 +1686,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;
 }
 
@@ -1634,14 +1722,14 @@ _tap_gesture_check_finish(Gesture_Info *gesture)
  * @ingroup Elm_Gesture_Layer
  */
 static void
-_tap_gesture_finish(void *data)
+_tap_gesture_finish(void *data, Evas_Coord tap_finger_size)
 {
    /* This function will test each tap gesture when timer expires */
    Elm_Gesture_State s = ELM_GESTURE_STATE_ABORT;
    Gesture_Info *gesture = data;
    Taps_Type *st = gesture->data;
 
-   if (_tap_gesture_check_finish(gesture))
+   if (_tap_gesture_check_finish(gesture, tap_finger_size))
      {
         s = ELM_GESTURE_STATE_END;
      }
@@ -1667,13 +1755,16 @@ _multi_tap_timeout(void *data)
    ELM_GESTURE_LAYER_DATA_GET(data, sd);
 
    if (IS_TESTED(ELM_GESTURE_N_TAPS))
-     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_TAPS]);
+     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_TAPS],
+           sd->tap_finger_size);
 
    if (IS_TESTED(ELM_GESTURE_N_DOUBLE_TAPS))
-     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_DOUBLE_TAPS]);
+     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_DOUBLE_TAPS],
+           sd->tap_finger_size);
 
    if (IS_TESTED(ELM_GESTURE_N_TRIPLE_TAPS))
-     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_TRIPLE_TAPS]);
+     _tap_gesture_finish(sd->gesture[ELM_GESTURE_N_TRIPLE_TAPS],
+           sd->tap_finger_size);
 
    _clear_if_finished(data);
    sd->gest_taps_timeout = NULL;
@@ -1695,6 +1786,9 @@ static Eina_Bool
 _long_tap_timeout(void *data)
 {
    Gesture_Info *gesture = data;
+   Long_Tap_Type *st = gesture->data;
+
+   st->info.timestamp = ecore_time_get() * 1000;
 
    _state_set(gesture, ELM_GESTURE_STATE_MOVE,
               gesture->data, EINA_TRUE);
@@ -1727,7 +1821,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 */
@@ -1742,26 +1836,28 @@ _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;
    if (!st) /* Allocated once on first time */
      {
         st = calloc(1, sizeof(Taps_Type));
+        if (!st) return;
+
         gesture->data = st;
         _tap_gestures_test_reset(gesture);
      }
@@ -1770,94 +1866,161 @@ _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 (!_match_fingers_compare(st->l, pe, sd->tap_finger_size))
+                {  /* 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. */
+              _state_set(gesture, ELM_GESTURE_STATE_ABORT,
+                    &st->info, EINA_FALSE);
+           }
+
+         if (gesture->state == ELM_GESTURE_STATE_MOVE)
+           {  /* Report MOVE if all devices have same DOWN/UP count */
+              /* Should be in MOVE state from last UP event */
+              Eina_List *l;
+              Eina_Bool move = EINA_TRUE;
+              unsigned int n = 0;
+
+              EINA_LIST_FOREACH(st->l, l, pe_list)
+                {
+                   if (n == 0)
+                     {
+                        n = eina_list_count(pe_list);
+                     }
+                   else if (n != eina_list_count(pe_list))
+                     {
+                        move = EINA_FALSE;
+                     }
+                }
+
+              if (move && (n > 0))
+                {
+                   _state_set(gesture, ELM_GESTURE_STATE_MOVE,
+                         &st->info, EINA_TRUE);
+                }
+           }
+
+         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;
+
+         _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)))
+           {  /* Test for finish immidiatly, not waiting for timeout */
+              if (_tap_gesture_check_finish(gesture, sd->tap_finger_size))
+                {
+                   _tap_gesture_finish(gesture, sd->tap_finger_size);
+                   return;
+                }
+           }
 
-        pe_list = _pointer_event_record
-            (st, pe_list, pe, sd, event_info, event_type);
+         if ((gesture->state == ELM_GESTURE_STATE_START) ||
+               (gesture->state == ELM_GESTURE_STATE_MOVE))
+           {  /* Tap gesture started, no finger on surface. Report MOVE */
+              Eina_List *l;
+              Eina_Bool move = EINA_TRUE;
+              unsigned int n = 0;
 
-        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;
-               }
-          }
+              /* Report move only if all devices have same DOWN/UP count */
+              EINA_LIST_FOREACH(st->l, l, pe_list)
+                {
+                   if (n == 0)
+                     {
+                        n = eina_list_count(pe_list);
+                     }
+                   else if (n != eina_list_count(pe_list))
+                     {
+                        move = EINA_FALSE;
+                     }
+                }
 
-        break;
+              if ((move && (n > 0)) && (n < st->n_taps_needed))
+                {  /* Set number of fingers and report MOVE */
+                   /* We don't report MOVE when (n >= st->n_taps_needed)
+                      because will be END or ABORT at this stage */
+                   st->info.n = eina_list_count(st->l);
+                   _state_set(gesture, ELM_GESTURE_STATE_MOVE,
+                         &st->info, EINA_TRUE);
+                }
+           }
+
+         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,
+                            sd->tap_finger_size))
+                     {
+                        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;
      }
 }
 
@@ -1941,6 +2104,8 @@ _n_long_tap_test(Evas_Object *obj,
    if (!st) /* Allocated once on first time */
      {
         st = calloc(1, sizeof(Long_Tap_Type));
+        if (!st) return;
+
         gesture->data = st;
         _n_long_tap_test_reset(gesture);
      }
@@ -1956,13 +2121,13 @@ _n_long_tap_test(Evas_Object *obj,
         _compute_taps_center(st, &st->info.x, &st->info.y, pe);
         st->center_x = st->info.x;  /* Update coords for */
         st->center_y = st->info.y;  /* reporting START  */
+        st->info.timestamp = pe->timestamp;
 
         /* This is the first mouse down we got */
         if (eina_list_count(st->touched) == 1)
           {
              _state_set(gesture, ELM_GESTURE_STATE_START,
                    gesture->data, EINA_FALSE);
-             st->info.timestamp = pe->timestamp;
 
              /* To test long tap */
              /* When this timer expires, gesture STARTED */
@@ -1982,6 +2147,7 @@ _n_long_tap_test(Evas_Object *obj,
       case EVAS_CALLBACK_MOUSE_UP:
         st->touched = _touched_device_remove(st->touched, pe);
         _compute_taps_center(st, &st->center_x, &st->center_y, pe);
+        st->info.timestamp = pe->timestamp;
         if (st->info.n)
           {
              if (gesture->state == ELM_GESTURE_STATE_MOVE)
@@ -1991,11 +2157,7 @@ _n_long_tap_test(Evas_Object *obj,
                ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
                                     &st->info, EINA_FALSE);
 
-             if (st->timeout)
-               {
-                  ecore_timer_del(st->timeout);
-                  st->timeout = NULL;
-               }
+             ELM_SAFE_FREE(st->timeout, ecore_timer_del);
              _event_consume(sd, event_info, event_type, ev_flag);
           }
 
@@ -2012,14 +2174,12 @@ _n_long_tap_test(Evas_Object *obj,
              Evas_Coord y = 0;
 
              _compute_taps_center(st, &x, &y, pe);
+             st->info.timestamp = pe->timestamp;
              /* ABORT if user moved fingers out of tap area */
-             if (!_inside(x, y, st->center_x, st->center_y))
+             if (!_inside(x, y, st->center_x, st->center_y,
+                      sd->tap_finger_size))
                {
-                  if (st->timeout)
-                    {
-                       ecore_timer_del(st->timeout);
-                       st->timeout = NULL;
-                    }
+                  ELM_SAFE_FREE(st->timeout, ecore_timer_del);
 
                   /* Report MOVE if gesture started */
                   ev_flag = _state_set(gesture, ELM_GESTURE_STATE_ABORT,
@@ -2042,10 +2202,10 @@ _n_long_tap_test(Evas_Object *obj,
  * This momentum value will be sent to widget when gesture is completed.
  *
  * @param momentum pointer to buffer where we record momentum value.
- * @param x1 x coord where user started gesture.
- * @param y1 y coord where user started gesture.
- * @param x2 x coord where user completed gesture.
- * @param y2 y coord where user completed gesture.
+ * @param xx1 x coord where user started gesture.
+ * @param yy1 y coord where user started gesture.
+ * @param xx2 x coord where user completed gesture.
+ * @param yy2 y coord where user completed gesture.
  * @param t1x timestamp for X, when user started gesture.
  * @param t1y timestamp for Y, when user started gesture.
  * @param t2  timestamp when user completed gesture.
@@ -2095,12 +2255,12 @@ _momentum_set(Elm_Gesture_Momentum_Info *momentum,
  *
  * This function is used for computing rotation angle (DEG).
  *
- * @param x1 first finger x location.
- * @param y1 first finger y location.
- * @param x2 second finger x location.
- * @param y2 second finger y location.
+ * @param xx1 first finger x location.
+ * @param yy1 first finger y location.
+ * @param xx2 second finger x location.
+ * @param yy2 second finger y location.
  *
- * @return angle of the line between (x1,y1), (x2,y2) in Deg.
+ * @return angle of the line between (xx1,yy1), (xx2,yy2) in Deg.
  * Angles now are given in DEG, not RAD.
  * ZERO angle at 12-oclock, growing clockwise.
  *
@@ -2114,8 +2274,8 @@ _angle_get(Evas_Coord xx1,
 {
    double a, xx, yy, rt = (-1);
 
-   xx = fabs(xx2 - xx1);
-   yy = fabs(yy2 - yy1);
+   xx = abs(xx2 - xx1);
+   yy = abs(yy2 - yy1);
 
    if (((int)xx) && ((int)yy))
      {
@@ -2163,10 +2323,10 @@ _angle_get(Evas_Coord xx1,
  * This function is used for computing the magnitude and direction
  * of vector between two points.
  *
- * @param x1 first finger x location.
- * @param y1 first finger y location.
- * @param x2 second finger x location.
- * @param y2 second finger y location.
+ * @param xx1 first finger x location.
+ * @param yy1 first finger y location.
+ * @param xx2 second finger x location.
+ * @param yy2 second finger y location.
  * @param l length computed (output)
  * @param a angle computed (output)
  *
@@ -2244,6 +2404,8 @@ _momentum_test(Evas_Object *obj,
    if (!st) /* Allocated once on first time */
      {
         st = calloc(1, sizeof(Momentum_Type));
+        if (!st) return;
+
         gesture->data = st;
         _momentum_test_reset(gesture);
      }
@@ -2382,8 +2544,8 @@ _momentum_test(Evas_Object *obj,
         st->line_end.y = pe_local.y;
         st->t_end = pe_local.timestamp;
 
-        if ((fabs(st->info.mx) > ELM_GESTURE_MINIMUM_MOMENTUM) ||
-            (fabs(st->info.my) > ELM_GESTURE_MINIMUM_MOMENTUM))
+        if ((abs(st->info.mx) > ELM_GESTURE_MINIMUM_MOMENTUM) ||
+            (abs(st->info.my) > ELM_GESTURE_MINIMUM_MOMENTUM))
           state_to_report = ELM_GESTURE_STATE_END;
         else
           state_to_report = ELM_GESTURE_STATE_ABORT;
@@ -2529,6 +2691,8 @@ _n_line_test(Evas_Object *obj,
    if (!st)
      {
         st = calloc(1, sizeof(Line_Type));
+        if (!st) return;
+
         gesture->data = st;
      }
 
@@ -2831,14 +2995,14 @@ _on_rotation_broke_tolerance(Rotate_Type *st)
  * This function is used for computing the gap between fingers.
  * It returns the length and center point between fingers.
  *
- * @param x1 first finger x location.
- * @param y1 first finger y location.
- * @param x2 second finger x location.
- * @param y2 second finger y location.
- * @param x  Gets center point x cord (output)
- * @param y  Gets center point y cord (output)
+ * @param xx1 first finger x location.
+ * @param yy1 first finger y location.
+ * @param xx2 second finger x location.
+ * @param yy2 second finger y location.
+ * @param x  Get center point x cord (output)
+ * @param y  Get center point y cord (output)
  *
- * @return length of the line between (x1,y1), (x2,y2) in pixels.
+ * @return length of the line between (xx1,yy1), (xx2,yy2) in pixels.
  *
  * @ingroup Elm_Gesture_Layer
  */
@@ -2851,8 +3015,8 @@ _finger_gap_length_get(Evas_Coord xx1,
                        Evas_Coord *y)
 {
    double a, b, xx, yy, gap;
-   xx = fabs(xx2 - xx1);
-   yy = fabs(yy2 - yy1);
+   xx = abs(xx2 - xx1);
+   yy = abs(yy2 - yy1);
    gap = sqrt((xx * xx) + (yy * yy));
 
    /* START - Compute zoom center point */
@@ -2957,10 +3121,10 @@ _zoom_momentum_get(Zoom_Type *st,
  * This function is used for computing zoom value.
  *
  * @param st Pointer to zoom data based on user input.
- * @param x1 first finger x location.
- * @param y1 first finger y location.
- * @param x2 second finger x location.
- * @param y2 second finger y location.
+ * @param xx1 first finger x location.
+ * @param yy1 first finger y location.
+ * @param xx2 second finger x location.
+ * @param yy2 second finger y location.
  * @param factor zoom-factor, used to determine how fast zoom works.
  *
  * @return zoom value, when 1.0 means no zoom, 0.5 half size...
@@ -3049,6 +3213,8 @@ _zoom_with_wheel_test(Evas_Object *obj,
    if (!st) /* Allocated once on first time, used for zoom intermediate data */
      {
         st = calloc(1, sizeof(Zoom_Type));
+        if (!st) return;
+
         gesture_zoom->data = st;
         _zoom_test_reset(gesture_zoom);
      }
@@ -3058,9 +3224,9 @@ _zoom_with_wheel_test(Evas_Object *obj,
       case EVAS_CALLBACK_KEY_UP:
       {
          Evas_Event_Key_Up *p = event_info;
-         if ((!strcmp(p->keyname, "Control_L")) ||
+         if ((!strcmp(p->key, "Control_L")) ||
              /* Test if we ended a zoom gesture when releasing CTRL */
-             (!strcmp(p->keyname, "Control_R")))
+             (!strcmp(p->key, "Control_R")))
            {
               if ((st->zoom_wheel) &&
                   ((gesture_zoom->state == ELM_GESTURE_STATE_START) ||
@@ -3193,6 +3359,8 @@ _zoom_test(Evas_Object *obj,
    if (!st) /* Allocated once on first time, used for zoom data */
      {
         st = calloc(1, sizeof(Zoom_Type));
+        if (!st) return;
+
         gesture_zoom->data = st;
         _zoom_test_reset(gesture_zoom);
      }
@@ -3207,7 +3375,7 @@ _zoom_test(Evas_Object *obj,
         if ((!sd->glayer_continues_enable) &&
             (!st->zoom_st.timestamp))
           return;
-
+        // fallthrough is intentional
       case EVAS_CALLBACK_MOUSE_DOWN:
       case EVAS_CALLBACK_MULTI_DOWN:
       { /* Here we take care of zoom-start and zoom move */
@@ -3440,6 +3608,8 @@ _rotate_test(Evas_Object *obj,
    if (!st) /* Allocated once on first time */
      {
        st = calloc(1, sizeof(Rotate_Type));
+       if (!st) return;
+
        gesture->data = st;
        _rotate_test_reset(gesture);
      }
@@ -3452,7 +3622,7 @@ _rotate_test(Evas_Object *obj,
         if ((!sd->glayer_continues_enable) &&
             (!st->rotate_st.timestamp))
           return;
-
+        // fallthrough is intentional
       case EVAS_CALLBACK_MOUSE_DOWN:
       case EVAS_CALLBACK_MULTI_DOWN:
       { /* Here we take care of rotate-start and rotate move */
@@ -3571,24 +3741,22 @@ _rotate_test(Evas_Object *obj,
      }
 }
 
-static void
-_elm_gesture_layer_smart_disable(Eo *obj, void *_pd EINA_UNUSED, va_list *list)
+EOLIAN static Eina_Bool
+_elm_gesture_layer_elm_widget_disable(Eo *obj, Elm_Gesture_Layer_Data *_pd EINA_UNUSED)
 {
-   Eina_Bool *ret = va_arg(*list, Eina_Bool *);
    if (elm_widget_disabled_get(obj))
      _callbacks_unregister(obj);
    else
      _callbacks_register(obj);
 
-   if (ret) *ret = EINA_TRUE;
+   return EINA_TRUE;
 }
 
-static void
-_elm_gesture_layer_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+EOLIAN static void
+_elm_gesture_layer_evas_object_smart_add(Eo *obj, Elm_Gesture_Layer_Data *priv)
 {
-   eo_do_super(obj, evas_obj_smart_add());
-
-   Elm_Gesture_Layer_Smart_Data *priv = _pd;
+   eo_do_super(obj, MY_CLASS, evas_obj_smart_add());
+   elm_widget_sub_object_parent_add(obj);
 
    priv->line_min_length =
      _elm_config->glayer_line_min_length * elm_config_finger_size_get();
@@ -3615,33 +3783,53 @@ _elm_gesture_layer_smart_add(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
    memset(priv->gesture, 0, sizeof(priv->gesture));
 }
 
-static void
-_elm_gesture_layer_smart_del(Eo *obj, void *_pd, va_list *list EINA_UNUSED)
+static void _cbs_clean(Elm_Gesture_Layer_Data *sd, Elm_Gesture_Type idx, Elm_Gesture_State cb_type);
+
+EOLIAN static void
+_elm_gesture_layer_evas_object_smart_del(Eo *obj, Elm_Gesture_Layer_Data *sd)
 {
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
    Pointer_Event *data;
    int i;
 
-   _event_history_clear(obj);
-   eina_list_free(sd->pending);
-
-   EINA_LIST_FREE(sd->touched, data)
-     free(data);
-
-   if (!elm_widget_disabled_get(obj))
-     _callbacks_unregister(obj);
+   /* Clear all gestures intermediate data, stop any timers */
+   {
+      /* FIXME: +1 because of the mistake in the enum. */
+      Gesture_Info **gitr = sd->gesture + 1;
+      Tests_Array_Funcs *fitr = _glayer_tests_array + 1;
+      for (; fitr->reset; fitr++, gitr++)
+        {
+           if (IS_TESTED_GESTURE(*gitr))
+             fitr->reset(*gitr);
+        }
+   }
 
-   /* Free all gestures internal data structures */
+   /* First Free all gestures internal data structures */
    for (i = 0; i < ELM_GESTURE_LAST; i++)
      if (sd->gesture[i])
        {
           if (sd->gesture[i]->data)
             free(sd->gesture[i]->data);
 
+          _cbs_clean(sd, i, ELM_GESTURE_STATE_START);
+          _cbs_clean(sd, i, ELM_GESTURE_STATE_MOVE);
+          _cbs_clean(sd, i, ELM_GESTURE_STATE_END);
+          _cbs_clean(sd, i, ELM_GESTURE_STATE_ABORT);
           free(sd->gesture[i]);
+          sd->gesture[i] = NULL; /* Referenced by _event_history_clear */
        }
+   ecore_timer_del(sd->gest_taps_timeout);
+
+   /* Then take care of clearing events */
+   _event_history_clear(obj);
+   sd->pending = eina_list_free(sd->pending);
+
+   EINA_LIST_FREE(sd->touched, data)
+     free(data);
 
-   eo_do_super(obj, evas_obj_smart_del());
+   if (!elm_widget_disabled_get(obj))
+     _callbacks_unregister(obj);
+
+   eo_do_super(obj, MY_CLASS, evas_obj_smart_del());
 }
 
 EAPI Evas_Object *
@@ -3649,145 +3837,62 @@ elm_gesture_layer_add(Evas_Object *parent)
 {
    EINA_SAFETY_ON_NULL_RETURN_VAL(parent, NULL);
    Evas_Object *obj = eo_add(MY_CLASS, parent);
-   eo_unref(obj);
    return obj;
 }
 
-static void
-_constructor(Eo *obj, void *_pd EINA_UNUSED, va_list *list EINA_UNUSED)
+EOLIAN static Eo *
+_elm_gesture_layer_eo_base_constructor(Eo *obj, Elm_Gesture_Layer_Data *_pd EINA_UNUSED)
 {
-   eo_do_super(obj, eo_constructor());
-   eo_do(obj, evas_obj_type_set(MY_CLASS_NAME));
+   obj = eo_do_super_ret(obj, MY_CLASS, obj, eo_constructor());
+   eo_do(obj, evas_obj_type_set(MY_CLASS_NAME_LEGACY));
 
-   if (!elm_widget_sub_object_add(eo_parent_get(obj), obj))
-     ERR("could not add %p as sub object of %p", obj, eo_parent_get(obj));
-}
-
-EAPI Eina_Bool
-elm_gesture_layer_hold_events_get(const Evas_Object *obj)
-{
-   ELM_GESTURE_LAYER_CHECK(obj) EINA_FALSE;
-   Eina_Bool ret = EINA_FALSE;
-   eo_do((Eo *) obj, elm_obj_gesture_layer_hold_events_get(&ret));
-   return ret;
-}
-
-static void
-_hold_events_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
-{
-   Eina_Bool *ret = va_arg(*list, Eina_Bool *);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-   *ret = !sd->repeat_events;
+   return obj;
 }
 
-EAPI void
-elm_gesture_layer_hold_events_set(Evas_Object *obj,
-                                  Eina_Bool hold_events)
+EOLIAN static Eina_Bool
+_elm_gesture_layer_hold_events_get(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd)
 {
-   ELM_GESTURE_LAYER_CHECK(obj);
-   eo_do(obj, elm_obj_gesture_layer_hold_events_set(hold_events));
+   return !sd->repeat_events;
 }
 
-static void
-_hold_events_set(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
+EOLIAN static void
+_elm_gesture_layer_hold_events_set(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd, Eina_Bool hold_events)
 {
-   Eina_Bool hold_events = va_arg(*list, int);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-
    sd->repeat_events = !(!!hold_events);
 }
 
-EAPI double
-elm_gesture_layer_zoom_step_get(const Evas_Object *obj)
-{
-   ELM_GESTURE_LAYER_CHECK(obj) 0;
-   double ret = 0;
-   eo_do((Eo *) obj, elm_obj_gesture_layer_zoom_step_get(&ret));
-   return ret;
-}
-
-static void
-_zoom_step_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
-{
-   double *ret = va_arg(*list, double *);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-   *ret = sd->zoom_step;
-}
-
-EAPI void
-elm_gesture_layer_zoom_step_set(Evas_Object *obj,
-                                double step)
+EOLIAN static double
+_elm_gesture_layer_zoom_step_get(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd)
 {
-   ELM_GESTURE_LAYER_CHECK(obj);
-   eo_do(obj, elm_obj_gesture_layer_zoom_step_set(step));
+   return sd->zoom_step;
 }
 
-static void
-_zoom_step_set(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
+EOLIAN static void
+_elm_gesture_layer_zoom_step_set(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd, double step)
 {
-   double step = va_arg(*list, double);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-
    if (step < 0) return;
 
    sd->zoom_step = step;
 }
 
-EAPI double
-elm_gesture_layer_rotate_step_get(const Evas_Object *obj)
+EOLIAN static double
+_elm_gesture_layer_rotate_step_get(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd)
 {
-   ELM_GESTURE_LAYER_CHECK(obj) 0;
-   double ret = 0;
-   eo_do((Eo *) obj, elm_obj_gesture_layer_rotate_step_get(&ret));
-   return ret;
+   return sd->rotate_step;
 }
 
-static void
-_rotate_step_get(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
+EOLIAN static void
+_elm_gesture_layer_rotate_step_set(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd, double step)
 {
-   double *ret = va_arg(*list, double *);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-   *ret = sd->rotate_step;
-}
-
-EAPI void
-elm_gesture_layer_rotate_step_set(Evas_Object *obj,
-                                  double step)
-{
-   ELM_GESTURE_LAYER_CHECK(obj);
-   eo_do(obj, elm_obj_gesture_layer_rotate_step_set(step));
-}
-
-static void
-_rotate_step_set(Eo *obj EINA_UNUSED, void *_pd, va_list *list)
-{
-   double step = va_arg(*list, double);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-
    if (step < 0) return;
 
    sd->rotate_step = step;
 }
 
-EAPI Eina_Bool
-elm_gesture_layer_attach(Evas_Object *obj,
-                         Evas_Object *target)
+EOLIAN static Eina_Bool
+_elm_gesture_layer_attach(Eo *obj, Elm_Gesture_Layer_Data *sd, Evas_Object *target)
 {
-   ELM_GESTURE_LAYER_CHECK(obj) EINA_FALSE;
-   Eina_Bool ret = EINA_FALSE;
-   eo_do(obj, elm_obj_gesture_layer_attach(target, &ret));
-   return ret;
-}
-
-static void
-_attach(Eo *obj, void *_pd, va_list *list)
-{
-   Evas_Object *target = va_arg(*list, Evas_Object *);
-   Eina_Bool *ret = va_arg(*list, Eina_Bool *);
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
-   if (ret) *ret = EINA_FALSE;
-
-   if (!target) return;
+   if (!target) return EINA_FALSE;
 
    /* if was attached before, unregister callbacks first */
    if (sd->target)
@@ -3796,44 +3901,86 @@ _attach(Eo *obj, void *_pd, va_list *list)
    sd->target = target;
 
    _callbacks_register(obj);
-   if (ret) *ret = EINA_TRUE;
+   return EINA_TRUE;
 }
 
-EAPI void
-elm_gesture_layer_cb_set(Evas_Object *obj,
-                         Elm_Gesture_Type idx,
-                         Elm_Gesture_State cb_type,
-                         Elm_Gesture_Event_Cb cb,
-                         void *data)
+static void
+_cbs_clean(Elm_Gesture_Layer_Data *sd,
+          Elm_Gesture_Type idx,
+          Elm_Gesture_State cb_type)
 {
-   ELM_GESTURE_LAYER_CHECK(obj);
-   eo_do(obj, elm_obj_gesture_layer_cb_set(idx, cb_type, cb, data));
+   if (!sd->gesture[idx]) return;
+
+   Func_Data *cb_info;
+   EINA_INLIST_FREE(sd->gesture[idx]->cbs[cb_type], cb_info)
+     {
+        sd->gesture[idx]->cbs[cb_type] = eina_inlist_remove(
+              sd->gesture[idx]->cbs[cb_type], EINA_INLIST_GET(cb_info));
+        free(cb_info);
+     }
+   SET_TEST_BIT(sd->gesture[idx]);
 }
 
-static void
-_cb_set(Eo *obj, void *_pd, va_list *list)
+EOLIAN static void
+_elm_gesture_layer_cb_set(Eo *obj, Elm_Gesture_Layer_Data *sd, Elm_Gesture_Type idx, Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
 {
-   Elm_Gesture_Type idx = va_arg(*list, Elm_Gesture_Type);
-   Elm_Gesture_State cb_type = va_arg(*list, Elm_Gesture_State);
-   Elm_Gesture_Event_Cb cb = va_arg(*list, Elm_Gesture_Event_Cb);
-   void *data = va_arg(*list, void *);
+   /* Clear gesture intermediate data, stop any timers */
+   if (IS_TESTED_GESTURE(sd->gesture[idx]))
+     _glayer_tests_array[idx].reset(sd->gesture[idx]);
+
+   _cbs_clean(sd, idx, cb_type); // for ABI compat.
+   eo_do(obj, elm_obj_gesture_layer_cb_add(idx, cb_type, cb, data));
+}
+
+EOLIAN static void
+_elm_gesture_layer_cb_add(Eo *obj, Elm_Gesture_Layer_Data *sd, Elm_Gesture_Type idx, Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
+{
+   if (!cb) return;
 
    Gesture_Info *p;
-   Elm_Gesture_Layer_Smart_Data *sd = _pd;
 
    if (!sd->gesture[idx])
      sd->gesture[idx] = calloc(1, sizeof(Gesture_Info));
    if (!sd->gesture[idx]) return;
 
+   Func_Data *cb_info = calloc(1, sizeof(*cb_info));
+   if (!cb_info) return;
+   cb_info->cb = cb;
+   cb_info->user_data = data;
+
    p = sd->gesture[idx];
    p->obj = obj;
    p->g_type = idx;
-   p->fn[cb_type].cb = cb;
-   p->fn[cb_type].user_data = data;
+   p->cbs[cb_type] = eina_inlist_append(p->cbs[cb_type],
+         EINA_INLIST_GET(cb_info));
    p->state = ELM_GESTURE_STATE_UNDEFINED;
    SET_TEST_BIT(p);
 }
 
+EOLIAN static void
+_elm_gesture_layer_cb_del(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd, Elm_Gesture_Type idx, Elm_Gesture_State cb_type, Elm_Gesture_Event_Cb cb, void *data)
+{
+   if (!sd->gesture[idx]) return;
+
+   Eina_Inlist *itr;
+   Func_Data *cb_info;
+   EINA_INLIST_FOREACH_SAFE(sd->gesture[idx]->cbs[cb_type], itr, cb_info)
+     {
+        if (cb_info->cb == cb && cb_info->user_data == data)
+          {
+             /* Clear gesture intermediate data, stop any timers */
+             if (IS_TESTED_GESTURE(sd->gesture[idx]))
+                _glayer_tests_array[idx].reset(sd->gesture[idx]);
+
+             sd->gesture[idx]->cbs[cb_type] = eina_inlist_remove(
+                   sd->gesture[idx]->cbs[cb_type], EINA_INLIST_GET(cb_info));
+             free(cb_info);
+             SET_TEST_BIT(sd->gesture[idx]);
+             return;
+          }
+     }
+}
+
 EAPI void
 elm_gesture_layer_line_min_length_set(Evas_Object *obj, int line_min_length)
 {
@@ -3842,7 +3989,6 @@ elm_gesture_layer_line_min_length_set(Evas_Object *obj, int line_min_length)
    sd->line_min_length = line_min_length;
 }
 
-
 EAPI int
 elm_gesture_layer_line_min_length_get(const Evas_Object *obj)
 {
@@ -4011,53 +4157,25 @@ elm_gesture_layer_double_tap_timeout_get(const Evas_Object *obj)
    return sd->double_tap_timeout;
 }
 
-static void
-_class_constructor(Eo_Class *klass)
-{
-   const Eo_Op_Func_Description func_desc[] = {
-        EO_OP_FUNC(EO_BASE_ID(EO_BASE_SUB_ID_CONSTRUCTOR), _constructor),
-
-        EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_ADD), _elm_gesture_layer_smart_add),
-        EO_OP_FUNC(EVAS_OBJ_SMART_ID(EVAS_OBJ_SMART_SUB_ID_DEL), _elm_gesture_layer_smart_del),
-
-        EO_OP_FUNC(ELM_WIDGET_ID(ELM_WIDGET_SUB_ID_DISABLE), _elm_gesture_layer_smart_disable),
-
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_HOLD_EVENTS_GET), _hold_events_get),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_HOLD_EVENTS_SET), _hold_events_set),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_ZOOM_STEP_GET), _zoom_step_get),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_ZOOM_STEP_SET), _zoom_step_set),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_ROTATE_STEP_GET), _rotate_step_get),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_ROTATE_STEP_SET), _rotate_step_set),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_ATTACH), _attach),
-        EO_OP_FUNC(ELM_OBJ_GESTURE_LAYER_ID(ELM_OBJ_GESTURE_LAYER_SUB_ID_CB_SET), _cb_set),
-        EO_OP_FUNC_SENTINEL
-   };
-   eo_class_funcs_set(klass, func_desc);
-
-   evas_smart_legacy_type_register(MY_CLASS_NAME, klass);
-}
-
-static const Eo_Op_Description op_desc[] = {
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_HOLD_EVENTS_GET, "Call this function to get repeat-events settings."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_HOLD_EVENTS_SET, "This function is to make gesture-layer repeat events."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_ZOOM_STEP_GET, "This function returns step-value for zoom action."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_ZOOM_STEP_SET, "This function sets step-value for zoom action."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_ROTATE_STEP_GET, "This function returns step-value for rotate action."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_ROTATE_STEP_SET, "This function sets step-value for rotate action."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_ATTACH, "Attach a given gesture layer widget to an Evas object, thus setting the widget's target."),
-     EO_OP_DESCRIPTION(ELM_OBJ_GESTURE_LAYER_SUB_ID_CB_SET, "Use function to set callbacks to be notified about change of state of gesture."),
-     EO_OP_DESCRIPTION_SENTINEL
-};
+EOLIAN static void
+_elm_gesture_layer_tap_finger_size_set(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd, Evas_Coord sz)
+{
+   if (sz < 0)
+      sz = 0;  /* Should not be negative, will reset to system value */
 
-static const Eo_Class_Description class_desc = {
-     EO_VERSION,
-     MY_CLASS_NAME,
-     EO_CLASS_TYPE_REGULAR,
-     EO_CLASS_DESCRIPTION_OPS(&ELM_OBJ_GESTURE_LAYER_BASE_ID, op_desc, ELM_OBJ_GESTURE_LAYER_SUB_ID_LAST),
-     NULL,
-     sizeof(Elm_Gesture_Layer_Smart_Data),
-     _class_constructor,
-     NULL
-};
+   sd->tap_finger_size = sz;
+}
+
+EOLIAN static Evas_Coord
+_elm_gesture_layer_tap_finger_size_get(Eo *obj EINA_UNUSED, Elm_Gesture_Layer_Data *sd)
+{
+   return sd->tap_finger_size;
+}
+
+static void
+_elm_gesture_layer_class_constructor(Eo_Class *klass)
+{
+   evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
+}
 
-EO_DEFINE_CLASS(elm_obj_gesture_layer_class_get, &class_desc, ELM_OBJ_WIDGET_CLASS, NULL);
+#include "elm_gesture_layer.eo.c"