#define EFL_INPUT_EVENT_PROTECTED

#include "evas_common_private.h"
#include "evas_private.h"

int _evas_event_counter = 0;

static Eina_List *
_evas_event_object_list_in_get(Evas *eo_e, Eina_List *in,
                               const Eina_Inlist *ilist,
                               const Eina_List *list,
                               Evas_Object *stop,
                               int x, int y, int *no_rep, Eina_Bool source);

/* FIXME: use eina_list_clone */
static Eina_List *
evas_event_list_copy(Eina_List *list);

static void
_canvas_event_feed_mouse_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev);

static void
_canvas_event_feed_multi_up_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev);

static void
_canvas_event_feed_multi_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev);

static void
_canvas_event_feed_mouse_move_legacy(Evas *eo_e, Evas_Public_Data *e, int x, int y,
                                     unsigned int timestamp, const void *data);

static inline void
_evas_event_feed_check(Evas_Public_Data *e)
{
   if (EINA_LIKELY(!e->running_post_events)) return;
   CRI("Feeding new input events from a post-event callback is risky!");
}

static inline Eina_Bool
_evas_event_object_pointer_allow(Eo *eo_obj, Evas_Object_Protected_Data *obj, Evas_Object_Pointer_Data *obj_pdata)
{
   return (obj->is_event_parent || evas_object_clippers_is_visible(eo_obj, obj) || obj_pdata->mouse_grabbed) &&
           (!evas_event_passes_through(eo_obj, obj)) &&
           (!evas_event_freezes_through(eo_obj, obj)) &&
           (!obj->clip.clipees);
}

static inline Eina_Bool
_evas_event_object_pointer_allow_precise(Eo *eo_obj, Evas_Object_Protected_Data *obj, int x, int y, const Eina_List *ins)
{
   return eina_list_data_find(ins, eo_obj) &&
     ((!obj->precise_is_inside) || evas_object_is_inside(eo_obj, obj, x, y));
}

#define EVAS_EVENT_FEED_SAFETY_CHECK(evas) _evas_event_feed_check(evas)

static Eina_Bool
_evas_event_havemap_adjust_f_inline(Evas_Object_Protected_Data *obj, Eina_Vector2 *point, Eina_Bool mouse_grabbed)
{
   Eina_Bool ret = EINA_FALSE;

   if (obj->smart.parent)
     {
        Evas_Object_Protected_Data *smart_parent_obj = efl_data_scope_get(obj->smart.parent, EFL_CANVAS_OBJECT_CLASS);
        ret |= _evas_event_havemap_adjust_f_inline(smart_parent_obj, point, mouse_grabbed);
     }

   if ((!obj->map->cur.usemap) || (!obj->map->cur.map)) return ret;

   //FIXME: Unless map_coords_get() supports grab mode and extrapolate coords
   //outside map, this should check the return value for outside case.

   //FIXME: When Mouse Out, it fails. but the coordiates might be transformed as well.
   if (evas_map_coords_get(obj->map->cur.map, point->x, point->y, &point->x, &point->y, mouse_grabbed))
     {
        point->x += obj->cur->geometry.x;
        point->y += obj->cur->geometry.y;
        return EINA_TRUE;
     }
   return ret;
}

static void
_evas_event_havemap_adjust_f(Evas_Object_Protected_Data *obj, Eina_Vector2 *cur_pt, Eina_Vector2 *prev_pt, Eina_Bool mouse_grabbed)
{
   Eina_Vector2 tmp_pt = *cur_pt;
   if (!_evas_event_havemap_adjust_f_inline(obj, &tmp_pt, mouse_grabbed)) return;

   prev_pt->x += (tmp_pt.x - cur_pt->x);
   prev_pt->y += (tmp_pt.y - cur_pt->y);
   *cur_pt = tmp_pt;
}

#if 0
# define DDD_DO 1
# define DDD(...) do { for (int _i = 0; _i < spaces; _i++) printf(" "); printf(__VA_ARGS__); } while (0)
# define D(...) do { printf(__VA_ARGS__); } while (0)
# define DDD_STATIC static
#else
# define DDD(...) do { } while (0)
# define D(...) do { } while (0)
# define DDD_STATIC
#endif

#ifdef DDD_DO
static void
walk_clippers_print(int spaces, Evas_Object_Protected_Data *obj)
{
   DDD("<<< CLIP %p c[%6i %6i %6ix%6i] c[%6i %6i %6ix%6i]\n",
       obj->object,
       obj->cur->geometry.x, obj->cur->geometry.y,
       obj->cur->geometry.w, obj->cur->geometry.h,
       obj->cur->cache.clip.x, obj->cur->cache.clip.y,
       obj->cur->cache.clip.w, obj->cur->cache.clip.h
      );
   if (obj->cur->clipper) walk_clippers_print(spaces + 1, obj->cur->clipper);
}
#endif

static void
clip_calc(Evas_Object_Protected_Data *obj, Eina_Rectangle *c)
{
   if (!obj) return;
   RECTS_CLIP_TO_RECT(c->x, c->y, c->w, c->h,
                      obj->cur->geometry.x, obj->cur->geometry.y,
                      obj->cur->geometry.w, obj->cur->geometry.h);
   clip_calc(obj->cur->clipper, c);
}

static Eina_List *
_evas_event_object_list_raw_in_get_single(Evas *eo_e, Evas_Object_Protected_Data *obj, Eina_List *in, Evas_Object *stop,
                                          int x, int y, int *no_rep, Eina_Bool source, int spaces EINA_UNUSED)
{
   Eina_Rectangle c;
   int inside;
   Evas_Object *eo_obj = obj->object;
   if (eo_obj == stop)
     {
        *no_rep = 1;
        DDD("***** NO REP - STOP *****\n");
        return in;
     }
   if ((!obj->cur->visible) && (!obj->is_event_parent)) return in;
   // XXX: this below DYNAMICALLY calculates the current clip rect
   // by walking clippers to each parent clipper until there are
   // no more of them. this is a necessary hack because cache.clip
   // cooreds are broken. somewhere along the way in the past few years
   // someone has forgotten to flag them as dirty and update them
   // so a clicp recalce caqn work... somewhere. maybe a prexy or map fix
   // or an optimization. finding this is really hard, so i'm going
   // for plan b and doing this on the fly. it's only for event or
   // callback handling so its a small percentage of the time, but
   // it's better that we get this right

   if (EINA_UNLIKELY((!!obj->map) && (obj->map->cur.map)
                     && (obj->map->cur.usemap)))
     c = obj->map->cur.map->normal_geometry;
   else
     {
        if (obj->is_smart)
          {
             Eina_Rectangle bounding_box = { 0, };

             evas_object_smart_bounding_box_update(obj);
             evas_object_smart_bounding_box_get(obj, &bounding_box, NULL);
             c = bounding_box;
          }
        else
          {
             if (obj->clip.clipees) return in;
             c = obj->cur->geometry;
          }
     }
   clip_calc(obj->cur->clipper, &c);
   // only worry about objects that intersect INCLUDING clippint
   if ((!RECTS_INTERSECT(x, y, 1, 1, c.x, c.y, c.w, c.h)) && (!obj->child_has_map))
     {
#ifdef DDD_DO
        if (obj->is_smart)
          {
             Eina_Rectangle bounding_box = { 0, 0, 0, 0 };

             evas_object_smart_bounding_box_get(obj, &bounding_box, NULL);
             DDD("___  %p g[%6i %6i %6ix%6i] c[%6i %6i %6ix%6i] b[%6i %6i %6ix%6i] %s\n",
                 obj->object,
                 obj->cur->geometry.x, obj->cur->geometry.y,
                 obj->cur->geometry.w, obj->cur->geometry.h,
                 obj->cur->cache.clip.x, obj->cur->cache.clip.y,
                 obj->cur->cache.clip.w, obj->cur->cache.clip.h,
                 bounding_box.x, bounding_box.y,
                 bounding_box.w, bounding_box.h,
                 obj->type);
          }
        else
          {
             DDD("___  %p g[%6i %6i %6ix%6i] c[%6i %6i %6ix%6i] %s\n",
                 obj->object,
                 obj->cur->geometry.x, obj->cur->geometry.y,
                 obj->cur->geometry.w, obj->cur->geometry.h,
                 obj->cur->cache.clip.x, obj->cur->cache.clip.y,
                 obj->cur->cache.clip.w, obj->cur->cache.clip.h,
                 obj->type);
          }
        if (!strcmp(obj->type, "e_layout"))
          {
             if (obj->cur->clipper)
               walk_clippers_print(spaces + 1, obj->cur->clipper);
          }
#endif
        return in;
     }
#ifdef DDD_DO
   else
     {
        if (obj->is_smart)
          {
             Eina_Rectangle bounding_box = { 0, 0, 0, 0 };

             evas_object_smart_bounding_box_get(obj, &bounding_box, NULL);
             DDD("OBJ  %p g[%6i %6i %6ix%6i] c[%6i %6i %6ix%6i] b[%6i %6i %6ix%6i] %s\n",
                 obj->object,
                 obj->cur->geometry.x, obj->cur->geometry.y,
                 obj->cur->geometry.w, obj->cur->geometry.h,
                 obj->cur->cache.clip.x, obj->cur->cache.clip.y,
                 obj->cur->cache.clip.w, obj->cur->cache.clip.h,
                 bounding_box.x, bounding_box.y,
                 bounding_box.w, bounding_box.h,
                 obj->type);
          }
        else
          {
             DDD("OBJ  %p g[%6i %6i %6ix%6i] c[%6i %6i %6ix%6i] %s\n",
                 obj->object,
                 obj->cur->geometry.x, obj->cur->geometry.y,
                 obj->cur->geometry.w, obj->cur->geometry.h,
                 obj->cur->cache.clip.x, obj->cur->cache.clip.y,
                 obj->cur->cache.clip.w, obj->cur->cache.clip.h,
                 obj->type);
          }
//             if (!strcmp(obj->type, "e_layout"))
          {
             if (obj->cur->clipper)
               walk_clippers_print(spaces + 1, obj->cur->clipper);
          }
     }
#endif

   if (!source)
     {
        if (evas_event_passes_through(eo_obj, obj)) return in;
        if (evas_object_is_source_invisible(eo_obj, obj)) return in;
     }
   if ((obj->delete_me == 0) &&
       ((source) || ((obj->cur->visible || obj->is_event_parent) && (!obj->clip.clipees) &&
        (obj->is_event_parent || evas_object_clippers_is_visible(eo_obj, obj)))))
     {
        if (obj->is_smart)
          {
             DDD("CHILDREN ->\n");
             Evas_Object_Protected_Data *clip = obj->cur->clipper;
             int norep = 0;

             if (clip && clip->mask->is_mask && clip->precise_is_inside)
               if (!evas_object_is_inside(clip->object, clip, x, y))
                 return in;

             if ((obj->map->cur.usemap) && (obj->map->cur.map))
               {
                  inside = evas_object_is_in_output_rect(eo_obj, obj, x, y, 1, 1);
                  if (inside)
                    {
                       if (!evas_map_coords_get(obj->map->cur.map, x, y,
                                                &(obj->map->cur.map->mx),
                                                &(obj->map->cur.map->my), 0))
                         {
                            inside = 0;
                         }
                       else
                         {
                            in = _evas_event_object_list_in_get
                               (eo_e, in,
                                evas_object_smart_members_get_direct(eo_obj),
                                NULL,
                                stop,
                                obj->cur->geometry.x + obj->map->cur.map->mx,
                                obj->cur->geometry.y + obj->map->cur.map->my,
                                &norep, source);
                         }
                    }
               }
             else
               {
                  Eina_Rectangle bounding_box = { 0, };

                  if (!obj->child_has_map)
                    evas_object_smart_bounding_box_update(obj);

                  evas_object_smart_bounding_box_get(obj, &bounding_box, NULL);

                  if (obj->child_has_map ||
                      (bounding_box.x <= x &&
                       bounding_box.x + bounding_box.w >= x &&
                       bounding_box.y <= y &&
                       bounding_box.y + bounding_box.h >= y) ||
                      (obj->cur->geometry.x <= x &&
                       obj->cur->geometry.x + obj->cur->geometry.w >= x &&
                       obj->cur->geometry.y <= y &&
                       obj->cur->geometry.y + obj->cur->geometry.h >= y))
                    in = _evas_event_object_list_in_get
                      (eo_e, in, evas_object_smart_members_get_direct(eo_obj), NULL,
                       stop, x, y, &norep, source);
               }
             if (norep)
               {
                  if (!obj->repeat_events)
                    {
                       *no_rep = 1;
                       DDD("***** NO REP1 *****\n");
                       return in;
                    }
               }
          }
        else if (obj->is_event_parent)
          {
             int norep = 0;
             in = _evas_event_object_list_in_get(eo_e, in,
               NULL, evas_object_event_grabber_members_list(eo_obj),
               stop, x, y, &norep, source);
             if (norep)
               {
                  if (!obj->repeat_events)
                    {
                       *no_rep = 1;
                       DDD("***** NO REP1 *****\n");
                       return in;
                    }
               }
          }
        else
          {
             Evas_Object_Protected_Data *clip = obj->cur->clipper;
             if (clip && clip->mask->is_mask && clip->precise_is_inside)
               inside = evas_object_is_inside(clip->object, clip, x, y);
             else
               inside = evas_object_is_in_output_rect(eo_obj, obj, x, y, 1, 1);

             if (inside)
               {
                  if ((obj->map->cur.usemap) && (obj->map->cur.map))
                    {
                       if (!evas_map_coords_get(obj->map->cur.map, x, y,
                                                &(obj->map->cur.map->mx),
                                                &(obj->map->cur.map->my), 0))
                         {
                            inside = 0;
                         }
                    }
               }
             if (inside && ((!obj->precise_is_inside) ||
                            (evas_object_is_inside(eo_obj, obj, x, y))))
               {
                  if (!evas_event_freezes_through(eo_obj, obj))
                    {
                       DDD("----------------> ADD obj %p\n", obj->object);
                       in = eina_list_append(in, eo_obj);
                    }
                  if (!obj->repeat_events)
                    {
                       *no_rep = 1;
                       DDD("***** NO REP2 *****\n");
                       return in;
                    }
               }
          }
     }
   return in;
}

static Eina_List *
_evas_event_object_list_raw_in_get(Evas *eo_e, Eina_List *in,
                                   const Eina_Inlist *ilist,
                                   const Eina_List *list,
                                   Evas_Object *stop,
                                   int x, int y, int *no_rep, Eina_Bool source,
                                   Eina_Bool must_walk_last)
{
   Evas_Object_Protected_Data *obj = NULL;
   DDD_STATIC int spaces = 0;

   if ((!ilist) && (!list)) return in;

   spaces++;
   if (ilist)
     {
        Eina_Inlist *last;

        if (must_walk_last) last = eina_inlist_last(ilist);
        else last = ilist->last;
        for (obj = _EINA_INLIST_CONTAINER(obj, last);
             obj;
             obj = _EINA_INLIST_CONTAINER(obj, EINA_INLIST_GET(obj)->prev))
          {
             if (obj->events->parent) continue;
             in = _evas_event_object_list_raw_in_get_single(eo_e, obj, in, stop, x, y, no_rep, source, spaces);
             if (*no_rep) goto end;
          }
     }
   else
     {
        Eina_List *l;

        EINA_LIST_REVERSE_FOREACH(list, l, obj)
          {
             in = _evas_event_object_list_raw_in_get_single(eo_e, obj, in, stop, x, y, no_rep, source, spaces);
             if (*no_rep) goto end;
          }
     }
   *no_rep = 0;

end:
   spaces--;
   return in;
}

static void
_transform_to_src_space_f(Evas_Object_Protected_Data *obj, Evas_Object_Protected_Data *src,
                          Eina_Vector2 *cur_pt, Eina_Vector2 *prev_pt)
{
   double obj_w = obj->cur->geometry.w, obj_h = obj->cur->geometry.h;
   double src_w = src->cur->geometry.w, src_h = src->cur->geometry.h;

   //Current Point
   cur_pt->x -= obj->cur->geometry.x;
   cur_pt->y -= obj->cur->geometry.y;

   if (!EINA_DBL_EQ(obj_w, src_w)) cur_pt->x *= (src_w / obj_w);
   if (!EINA_DBL_EQ(obj_h, src_h)) cur_pt->y *= (src_h / obj_h);

   cur_pt->x += src->cur->geometry.x;
   cur_pt->y += src->cur->geometry.y;

   //Prev Point
   prev_pt->x -= obj->cur->geometry.x;
   prev_pt->y -= obj->cur->geometry.y;

   if (!EINA_DBL_EQ(obj_w, src_w)) prev_pt->x *= (src_w / obj_w);
   if (!EINA_DBL_EQ(obj_h, src_h)) prev_pt->y *= (src_h / obj_h);

   prev_pt->x += src->cur->geometry.x;
   prev_pt->y += src->cur->geometry.y;
}

static Efl_Input_Device *
_evas_event_legacy_device_get(Eo *evas, Eina_Bool mouse)
{
   Efl_Input_Device *dev = _evas_device_top_get(evas);
   //The user did not push a device, use the default mouse/keyboard instead.
   if (!dev)
     {
        Evas_Public_Data *e = efl_data_scope_get(evas, EVAS_CANVAS_CLASS);
        if (mouse)
          return e->default_mouse;
        return e->default_keyboard;
     }
   return dev;
}

static void
_evas_event_source_mouse_down_events(Evas_Object *eo_obj, Evas *eo_e,
                                     Efl_Input_Pointer *parent_ev,
                                     Evas_Pointer_Data *pdata,
                                     int event_id)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   int addgrab = 0;
   int no_rep = 0;
   int srcgrab = 0;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;
   ev->touch_id = 0;

   EINA_LIST_FOREACH(src->proxy->src_event_in, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        srcgrab += obj_pdata->mouse_grabbed;
     }

   if (srcgrab == 0)
     {
        EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
          {
             if (proxy_write->src_event_in)
               proxy_write->src_event_in = eina_list_free(proxy_write->src_event_in);

             if (src->is_smart)
               {
                  proxy_write->src_event_in = _evas_event_object_list_raw_in_get
                     (eo_e, proxy_write->src_event_in,
                      evas_object_smart_members_get_direct(eo_src), NULL,
                      NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
               }
             else if (src->is_event_parent)
               {
                  proxy_write->src_event_in = _evas_event_object_list_raw_in_get
                     (eo_e, proxy_write->src_event_in,
                      NULL, evas_object_event_grabber_members_list(eo_src),
                      NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
               }
             else
               proxy_write->src_event_in = eina_list_append(proxy_write->src_event_in, eo_src);
          }
        EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);
     }

   if (pdata->seat->mouse_grabbed == 0)
     {
        if (pdata->seat->downs > 1) addgrab = pdata->seat->downs - 1;
     }

   EINA_LIST_FOREACH(src->proxy->src_event_in, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if ((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
            (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN))
          {
             obj_pdata->mouse_grabbed += (addgrab + 1);
             pdata->seat->mouse_grabbed += (addgrab + 1);
             if (obj_pdata->pointer_mode ==
                 EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
               {
                  pdata->seat->nogrep++;
                  break;
               }
          }
     }

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Pointer_Mode pointer_mode;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        pointer_mode = obj_pdata->pointer_mode;

        ev->cur = cur_pt;
        ev->prev = prev_pt;

        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_DOWN, evt,
                                        event_id, EFL_EVENT_POINTER_DOWN);
        if (e->delete_me) break;
        if (pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
          break;
     }
   eina_list_free(copy);

   efl_unref(evt);
}

static void
_evas_event_mouse_in_set(Evas_Pointer_Seat *pseat,
                         Evas_Object_Protected_Data *obj, Eina_Bool mouse_in)
{
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   if ((!pseat) || (!obj)) return;

   EINA_INLIST_FOREACH(pseat->pointers, pdata)
     {
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (obj_pdata)
          obj_pdata->mouse_in = mouse_in;
     }
}

static void
_evas_event_source_mouse_move_events(Evas_Object *eo_obj, Evas *eo_e,
                                     Efl_Input_Pointer *parent_ev,
                                     Evas_Pointer_Data *pdata,
                                     int event_id)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt_real, prev_pt_real, cur_pt, prev_pt;
   Evas_Object_Pointer_Data *obj_pdata;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   cur_pt_real = ev->cur;
   prev_pt_real = ev->prev;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;
   ev->touch_id = 0;

   if (pdata->seat->mouse_grabbed)
     {
        Eina_List *outs = NULL;
        Eina_List *copy = evas_event_list_copy(src->proxy->src_event_in);

        EINA_LIST_FOREACH(copy, l, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             //FIXME: When object is deleted in the src_event_in list,
             //the src_event_in list should be updated. But now there is no way.
             //So add checking NULL logic, please delete it if you make a better way.
             if (!child) continue;

             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }

             if (_evas_event_object_pointer_allow(eo_child, child, obj_pdata))
               {
                  ev->cur = cur_pt;
                  ev->prev = prev_pt;

                  _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  ev->action = EFL_POINTER_ACTION_MOVE;
                  evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                  event_id, EFL_EVENT_POINTER_MOVE);
               }
             else
               outs = eina_list_append(outs, eo_child);
             if (e->delete_me || e->is_frozen) break;
             //FIXME: take care nograb object
          }
        eina_list_free(copy);

        EINA_LIST_FREE(outs, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             if (child->delete_me) continue;
             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if ((obj_pdata->mouse_grabbed == 0) && (!e->delete_me))
               {
                  if (obj_pdata->mouse_in) continue; /* FIXME: dubious logic! */
                  _evas_event_mouse_in_set(pdata->seat, child, 0);
                  if (e->is_frozen) continue;

                  ev->cur = cur_pt_real;
                  ev->prev = prev_pt_real;

                  _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
                    proxy_write->src_event_in = eina_list_remove(proxy_write->src_event_in, eo_child);
                  EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);

                  ev->action = EFL_POINTER_ACTION_OUT;
                  evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
               }
          }
     }
   else
     {
        Eina_List *ins = NULL;
        Eina_List *copy = evas_event_list_copy(src->proxy->src_event_in);

        if (src->is_smart)
          {
             int no_rep = 0;
             ins = _evas_event_object_list_raw_in_get(eo_e, ins, evas_object_smart_members_get_direct(eo_src),
                                                      NULL, NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
          }
        else if (src->is_event_parent)
          {
             int no_rep = 0;
             ins = _evas_event_object_list_raw_in_get(eo_e, ins, NULL,
                                                      evas_object_event_grabber_members_list(eo_src),
                                                      NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
          }
        else
          ins = eina_list_append(ins, eo_src);

        EINA_LIST_FOREACH(copy, l, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             //FIXME: When object is deleted in the src_event_in list,
             //the src_event_in list should be updated. But now there is no way.
             //So add checking NULL logic, please delete it if you make a better way.
             if (!child) continue;

             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             ev->cur = cur_pt;
             ev->prev = prev_pt;

             _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if (evas_object_is_in_output_rect(eo_child, child,
                                               ev->cur.x, ev->cur.y, 1, 1) &&
                _evas_event_object_pointer_allow(eo_child, child, obj_pdata) &&
                _evas_event_object_pointer_allow_precise(eo_child, child, ev->cur.x, ev->cur.y, ins))
               {
                  ev->action = EFL_POINTER_ACTION_MOVE;
                 evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                  event_id, EFL_EVENT_POINTER_MOVE);
               }
             else if (obj_pdata->mouse_in)
               {
                  _evas_event_mouse_in_set(pdata->seat, child, 0);
                  if (e->is_frozen) continue;
                  ev->action = EFL_POINTER_ACTION_OUT;
                  evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
                  if (e->delete_me) break;

               }
             if (e->delete_me || e->is_frozen) break;
          }
        eina_list_free(copy);

        int event_id2 = _evas_object_event_new();
        EINA_LIST_FOREACH(ins, l, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
              if (!eina_list_data_find(src->proxy->src_event_in, eo_child))
                {
                   if (!obj_pdata->mouse_in)
                     {
                        _evas_event_mouse_in_set(pdata->seat, child, 1);
                        if (e->is_frozen) continue;

                        ev->cur = cur_pt;
                        ev->prev = prev_pt;

                        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                        ev->action = EFL_POINTER_ACTION_IN;
                        evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_IN, evt,
                                                        event_id2, EFL_EVENT_POINTER_IN);
                        if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                          {
                             ev->action = EFL_POINTER_ACTION_MOVE;
                             evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                             event_id2, EFL_EVENT_POINTER_MOVE);
                          }
                        if (e->delete_me) break;
                     }
                }
          }

        if (pdata->seat->mouse_grabbed == 0)
          {
             EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
               {
                  eina_list_free(proxy_write->src_event_in);
                  proxy_write->src_event_in = ins;
               }
             EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);
          }
        else
          {
             if (ins) eina_list_free(ins);
          }
     }

   efl_unref(evt);
}

static void
_evas_event_source_mouse_up_events(Evas_Object *eo_obj, Evas *eo_e,
                                   Efl_Input_Pointer *parent_ev,
                                   Evas_Pointer_Data *pdata,
                                   int event_id, Eina_Bool cancel)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;
   ev->touch_id = 0;

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Pointer_Mode pointer_mode;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if ((!cancel) && ((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
             (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)) &&
            (obj_pdata->mouse_grabbed > 0))
          {
             obj_pdata->mouse_grabbed--;
             pdata->seat->mouse_grabbed--;
          }

        pointer_mode = obj_pdata->pointer_mode;

        ev->cur = cur_pt;
        ev->prev = prev_pt;

        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        if (cancel)
          efl_event_callback_call(eo_child, EFL_EVENT_POINTER_CANCEL, evt);
        else
          evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_UP, evt,
                                          event_id, EFL_EVENT_POINTER_UP);
        if (e->delete_me) break;
        if (pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
          {
             if ((!cancel) && (pdata->seat->nogrep > 0)) pdata->seat->nogrep--;
             break;
          }
     }
   eina_list_free(copy);

   efl_unref(evt);
}

static void
_evas_event_source_hold_events(Evas_Object *eo_obj, int event_id, Efl_Input_Hold *evt)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *l;

   if (obj->layer->evas->is_frozen) return;

   EINA_LIST_FOREACH(src->proxy->src_event_in, l, eo_child)
     {
        if (src->delete_me) return;
        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        evas_object_event_callback_call(eo_child, child,
                                        EVAS_CALLBACK_HOLD, evt,
                                        event_id, EFL_EVENT_HOLD);
        if (src->layer->evas->delete_me) break;
     }
}

static void
_evas_event_source_wheel_events(Evas_Object *eo_obj, Evas *eo_e,
                                Efl_Input_Pointer *parent_ev, int event_id)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   Evas_Pointer_Data *pdata;

   if (obj->delete_me || src->delete_me || obj->layer->evas->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;

        if (src->delete_me) return;
        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        //FIXME: When object is deleted in the src_event_in list,
        //the src_event_in list should be updated. But now there is no way.
        //So add checking NULL logic, please delete it if you make a better way.
        if (!child) continue;

        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        ev->cur = cur_pt;
        ev->prev = prev_pt;
        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_WHEEL, evt,
                                        event_id, EFL_EVENT_POINTER_WHEEL);
        if (e->delete_me) break;
     }
   eina_list_free(copy);

   efl_unref(evt);
}

static void
_evas_event_source_multi_down_events(Evas_Object_Protected_Data *obj, Evas_Public_Data *e,
                                     Efl_Input_Pointer *parent_ev, Evas_Pointer_Data *pdata,
                                     int event_id)
{
   Evas_Object *eo_src = _evas_object_image_source_get(obj->object);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   Evas_Object_Pointer_Data *obj_pdata;
   int addgrab = 0;

   if (obj->delete_me || src->delete_me || obj->layer->evas->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = obj->object;
   ev->action = EFL_POINTER_ACTION_DOWN;

   if (pdata->seat->mouse_grabbed == 0)
     {
        if (pdata->seat->downs > 1)
          addgrab = pdata->seat->downs - 1;
        else  /* this is the first touch, which means it's also a move event, which means we need to redo this */
          {
             Eina_List *ins = NULL;

             if (src->is_smart)
               {
                  int no_rep = 0;
                  ins = _evas_event_object_list_raw_in_get
                        (e->evas, ins, evas_object_smart_members_get_direct(eo_src), NULL, NULL,
                         ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
               }
             if (src->is_event_parent)
               {
                  int no_rep = 0;
                  ins = _evas_event_object_list_raw_in_get
                        (e->evas, ins, NULL, evas_object_event_grabber_members_list(eo_src), NULL,
                         ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
               }
             else
               ins = eina_list_append(ins, eo_src);
             EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
               {
                  eina_list_free(proxy_write->src_event_in);
                  proxy_write->src_event_in = ins;
               }
             EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);
          }
     }

   EINA_LIST_FOREACH(src->proxy->src_event_in, l, eo_child)
     {
        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if (obj_pdata->pointer_mode != EVAS_OBJECT_POINTER_MODE_NOGRAB)
          {
             obj_pdata->mouse_grabbed += (addgrab + 1);
             pdata->seat->mouse_grabbed += (addgrab + 1);
          }
     }

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        ev->cur = cur_pt;
        ev->prev = prev_pt;

        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(obj->object, obj, EVAS_CALLBACK_MULTI_DOWN, evt,
                                        event_id, EFL_EVENT_FINGER_DOWN);
        if (e->delete_me) break;
     }
   eina_list_free(copy);

   efl_unref(evt);
}

static void
_evas_event_source_multi_up_events(Evas_Object_Protected_Data *obj, Evas_Public_Data *e,
                                   Efl_Input_Pointer *parent_ev, Evas_Pointer_Data *pdata,
                                   int event_id)
{
   Evas_Object *eo_src = _evas_object_image_source_get(obj->object);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;

   if (obj->delete_me || src->delete_me || obj->layer->evas->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;

   ev->source = obj->object;
   ev->action = EFL_POINTER_ACTION_UP;

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);

        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }

        if (((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
             (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)) &&
            (obj_pdata->mouse_grabbed > 0))
          {
             obj_pdata->mouse_grabbed--;
             pdata->seat->mouse_grabbed--;
          }
        ev->cur = cur_pt;
        ev->prev = prev_pt;
        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(obj->object, obj, EVAS_CALLBACK_MULTI_UP, evt,
                                        event_id, EFL_EVENT_FINGER_UP);
        if (e->delete_me || e->is_frozen) break;
     }
    eina_list_free(copy);

    efl_unref(evt);
}

static void
_evas_event_source_multi_move_events(Evas_Object_Protected_Data *obj, Evas_Public_Data *e,
                                     Efl_Input_Pointer *parent_ev, Evas_Pointer_Data *pdata,
                                     int event_id)
{
   Evas_Object *eo_src = _evas_object_image_source_get(obj->object);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   Evas_Object_Pointer_Data *obj_pdata;
   Evas *eo_e = e->evas;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = obj->object;
   ev->action = EFL_POINTER_ACTION_UP;

   /* Why a new event id here? Other 'source' events keep the same id. */
   event_id = _evas_object_event_new();

   if (pdata->seat->mouse_grabbed > 0)
     {
        copy = evas_event_list_copy(src->proxy->src_event_in);
        EINA_LIST_FOREACH(copy, l, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if (_evas_event_object_pointer_allow(eo_child, child, obj_pdata))
               {
                  ev->cur = cur_pt;
                  ev->prev = prev_pt;
                  _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  evas_object_event_callback_call(obj->object, obj, EVAS_CALLBACK_MULTI_MOVE, evt,
                                                  event_id, EFL_EVENT_FINGER_MOVE);
                  if (e->delete_me || e->is_frozen) break;
               }
          }
        eina_list_free(copy);
     }
   else
     {
        Eina_List *ins = NULL;

        if (src->is_smart)
          {
             int no_rep = 0;
             ins = _evas_event_object_list_raw_in_get
                   (eo_e, ins, evas_object_smart_members_get_direct(eo_src), NULL, NULL,
                    ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
          }
        if (src->is_event_parent)
          {
             int no_rep = 0;
             ins = _evas_event_object_list_raw_in_get
                   (eo_e, ins, NULL, evas_object_event_grabber_members_list(eo_src), NULL,
                    ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);
          }
        else
          ins = eina_list_append(ins, eo_src);

        copy = evas_event_list_copy(src->proxy->src_event_in);
        EINA_LIST_FOREACH(copy, l, eo_child)
          {
             child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, child);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }

             ev->cur = cur_pt;
             ev->prev = prev_pt;
             _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if (evas_object_is_in_output_rect(eo_child, child, ev->cur.x, ev->cur.y, 1, 1) &&
                _evas_event_object_pointer_allow(eo_child, child, obj_pdata) &&
                _evas_event_object_pointer_allow_precise(eo_child, child, ev->cur.x, ev->cur.y, ins))
               {
                  evas_object_event_callback_call(obj->object, obj, EVAS_CALLBACK_MULTI_MOVE, evt,
                                                  event_id, EFL_EVENT_FINGER_MOVE);
                  if (e->delete_me || e->is_frozen) break;
               }
          }
        eina_list_free(copy);
        if (pdata->seat->mouse_grabbed == 0)
          {
             EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
               {
                  eina_list_free(proxy_write->src_event_in);
                  proxy_write->src_event_in = ins;
               }
             EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);
          }
        else
          eina_list_free(ins);
     }

   efl_unref(evt);
}

static void
_evas_event_source_mouse_in_events(Evas_Object *eo_obj, Evas *eo_e,
                                   Efl_Input_Pointer *parent_ev, int event_id)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *ins = NULL, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   Evas_Pointer_Data *pdata;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;
   evt = efl_duplicate(parent_ev);

   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;
   ev->action = EFL_POINTER_ACTION_IN;

   if (src->is_smart)
     {
        int no_rep = 0;
        ins = _evas_event_object_list_raw_in_get(eo_e, ins, evas_object_smart_members_get_direct(eo_src),
                                                 NULL, NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);

     }
   else if (src->is_event_parent)
     {
        int no_rep = 0;
        ins = _evas_event_object_list_raw_in_get(eo_e, ins, NULL, evas_object_event_grabber_members_list(eo_src),
                                                 NULL, ev->cur.x, ev->cur.y, &no_rep, EINA_TRUE, EINA_FALSE);

     }
   else
     ins = eina_list_append(ins, eo_src);

   EINA_LIST_FOREACH(ins, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;

        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }

        if (!eina_list_data_find(src->proxy->src_event_in, eo_child))
          {
             if (obj_pdata->mouse_in) continue;
             _evas_event_mouse_in_set(pdata->seat, child, 1);

             ev->cur = cur_pt;
             ev->prev = prev_pt;
             _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             ev->action = EFL_POINTER_ACTION_IN;
             evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_IN, evt,
                                             event_id, EFL_EVENT_POINTER_IN);
             if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
               {
                  ev->action = EFL_POINTER_ACTION_MOVE;
                  evas_object_event_callback_call(eo_child, child, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                  event_id, EFL_EVENT_POINTER_MOVE);
               }
             if (e->delete_me || e->is_frozen) break;
          }
     }

   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
     {
       eina_list_free(proxy_write->src_event_in);
       proxy_write->src_event_in = ins;
     }
   EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);

   efl_unref(evt);
}

static void
_evas_event_source_mouse_out_events(Evas_Object *eo_obj, Evas *eo_e,
                                    Efl_Input_Pointer *parent_ev, int event_id)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
   Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Object_Protected_Data *child;
   Evas_Object *eo_child;
   Eina_List *copy, *l;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Eina_Vector2 cur_pt, prev_pt;
   Evas_Pointer_Data *pdata;

   if (obj->delete_me || src->delete_me || e->is_frozen) return;

   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   _transform_to_src_space_f(obj, src, &ev->cur, &ev->prev);

   cur_pt = ev->cur;
   prev_pt = ev->prev;
   ev->source = eo_obj;
   ev->action = EFL_POINTER_ACTION_OUT;

   copy = evas_event_list_copy(src->proxy->src_event_in);
   EINA_LIST_FOREACH(copy, l, eo_child)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, child);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }

        if (!obj_pdata->mouse_in) continue;
        _evas_event_mouse_in_set(pdata->seat, child, 0);
        if (child->delete_me) continue;

        ev->cur = cur_pt;
        ev->prev = prev_pt;
        _evas_event_havemap_adjust_f(child, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                        event_id, EFL_EVENT_POINTER_OUT);
        if (e->is_frozen) continue;
     }
   eina_list_free(copy);

   EINA_COW_WRITE_BEGIN(evas_object_proxy_cow, src->proxy, Evas_Object_Proxy_Data, proxy_write)
     proxy_write->src_event_in = eina_list_free(proxy_write->src_event_in);
   EINA_COW_WRITE_END(evas_object_proxy_cow, src->proxy, proxy_write);

   efl_unref(evt);
}

static Eina_List *
_evas_event_object_list_in_get(Evas *eo_e, Eina_List *in,
                               const Eina_Inlist *ilist,
                               const Eina_List *list,
                               Evas_Object *stop,
                               int x, int y, int *no_rep, Eina_Bool source)
{
   return _evas_event_object_list_raw_in_get(eo_e, in, ilist, list, stop, x, y,
                                             no_rep, source, EINA_FALSE);
}

static Eina_List *
_evas_event_objects_event_list_no_frozen_check(Evas *eo_e, Evas_Object *stop, int x, int y)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Evas_Layer *lay;
   Eina_List *in = NULL;

   if (!e->layers) return NULL;

   D("@@@@@ layer count = %i\n", eina_inlist_count(EINA_INLIST_GET(e->layers)));
   EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(e->layers)), lay)
     {
        int no_rep = 0;
        D("############################# check layer %i\n", lay->layer);
        in = _evas_event_object_list_in_get(eo_e, in,
                                            EINA_INLIST_GET(lay->objects), NULL,
                                            stop, x, y, &no_rep, EINA_FALSE);
        if (no_rep) return in;
     }
   return in;
}

EOLIAN Eina_List*
_evas_canvas_tree_objects_at_xy_get(Eo *eo_e, Evas_Public_Data *e EINA_UNUSED, Evas_Object *stop, int x, int y)
{
   return _evas_event_objects_event_list_no_frozen_check(eo_e, stop, x, y);
}

Eina_List *
evas_event_objects_event_list(Evas *eo_e, Evas_Object *stop, int x, int y)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if ((!e->layers) || (e->is_frozen)) return NULL;
   D("------------------------------GET EVETNS AT ............... %i %i\n", x, y);
   return _evas_event_objects_event_list_no_frozen_check(eo_e, stop, x, y);
}

static Eina_List *
evas_event_list_copy(Eina_List *list)
{
   Eina_List *l, *new_l = NULL;
   const void *data;

   EINA_LIST_FOREACH(list, l, data)
     new_l = eina_list_append(new_l, data);
   return new_l;
}
/* public functions */

EOLIAN void
_evas_canvas_event_default_flags_set(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Evas_Event_Flags flags)
{
   e->default_event_flags = flags;
}

EOLIAN Evas_Event_Flags
_evas_canvas_event_default_flags_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return e->default_event_flags;
}

static inline void
_canvas_event_thaw_eval_internal(Eo *eo_e, Evas_Public_Data *e)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, NULL);
   if (!pdata) return;
   _canvas_event_feed_mouse_move_legacy(eo_e, e, pdata->seat->x, pdata->seat->y,
                                        e->last_timestamp, NULL);
}

EVAS_API void
evas_event_freeze(Evas *eo_e)
{
   efl_event_freeze(eo_e);
}

EVAS_API void
evas_event_thaw(Evas *eo_e)
{
   efl_event_thaw(eo_e);
}

EOLIAN void
_evas_canvas_efl_object_event_freeze(Eo *eo_e, Evas_Public_Data *e)
{
   efl_event_freeze(efl_super(eo_e, EVAS_CANVAS_CLASS));
   e->is_frozen = EINA_TRUE;
}

EOLIAN void
_evas_canvas_efl_object_event_thaw(Eo *eo_e, Evas_Public_Data *e)
{
   int fcount = -1;
   efl_event_thaw(efl_super(eo_e, EVAS_CANVAS_CLASS));
   fcount = efl_event_freeze_count_get(efl_super(eo_e, EVAS_CANVAS_CLASS));
   if (0 == fcount)
     {
        Evas_Layer *lay;

        e->is_frozen = EINA_FALSE;
        EINA_INLIST_FOREACH((EINA_INLIST_GET(e->layers)), lay)
          {
             Evas_Object_Protected_Data *obj;

             EINA_INLIST_FOREACH(lay->objects, obj)
               {
                  evas_object_clip_recalc(obj);
                  evas_object_recalc_clippees(obj);
               }
          }

        _canvas_event_thaw_eval_internal(eo_e, e);
     }
}

EVAS_API int
evas_event_freeze_get(const Evas *eo_e)
{
   return efl_event_freeze_count_get(eo_e);
}

EVAS_API void
evas_event_thaw_eval(Evas *eo_e)
{
   if (!evas_event_freeze_get(eo_e))
     {
        EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
        Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
        _canvas_event_thaw_eval_internal(eo_e, e);
     }
}

static void
_canvas_event_feed_mouse_down_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id, b;
   Evas *eo_e;
   int addgrab = 0;
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   b = ev->button;
   DBG("ButtonEvent:down time=%u x=%d y=%d button=%d downs=%d",
       ev->timestamp, pdata->seat->x, pdata->seat->y, b, pdata->seat->downs);
   if ((b < 1) || (b > 32)) return;

   pdata->button |= (1u << (b - 1));
   pdata->seat->downs++;

   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;
   eo_e = e->evas;
   evt = ev->eo;

   event_id = _evas_object_event_new();

   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->touch_id = 0;
   ev->action = EFL_POINTER_ACTION_DOWN;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   /* append new touch point to the touch point list */
   _evas_touch_point_append(e->evas, 0, pdata->seat->x, pdata->seat->y);
   /* If this is the first finger down, i.e no other fingers pressed,
    * get a new event list, otherwise, keep the current grabbed list. */
   if (pdata->seat->mouse_grabbed == 0)
     {
        Eina_List *ins = evas_event_objects_event_list(eo_e,
                                                       NULL,
                                                       pdata->seat->x,
                                                       pdata->seat->y);
        /* free our old list of ins */
        eina_list_free(pdata->seat->object.in);
        /* and set up the new one */
        pdata->seat->object.in = ins;
        /* adjust grabbed count by the nuymber of currently held down
         * fingers/buttons */
        if (pdata->seat->downs > 1) addgrab = pdata->seat->downs - 1;
     }
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }

        if ((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
            (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN))
          {
             obj_pdata->mouse_grabbed += addgrab + 1;
             pdata->seat->mouse_grabbed += addgrab + 1;
             if (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
               {
                  pdata->seat->nogrep++;
                  break;
               }
          }
     }
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        Evas_Object_Pointer_Mode pointer_mode;
        if (obj->delete_me) continue;
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        pointer_mode = obj_pdata->pointer_mode;

        ev->cur.x = pdata->seat->x;
        ev->cur.y = pdata->seat->y;
        ev->prev.x = pdata->seat->prev.x;
        ev->prev.y = pdata->seat->prev.y;
        _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_DOWN, evt,
                                        event_id, EFL_EVENT_POINTER_DOWN);
        if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
          _evas_event_source_mouse_down_events(eo_obj, eo_e, evt, pdata, event_id);
        if (e->is_frozen || e->delete_me)  break;
        if (pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
          break;
     }
   if (copy) eina_list_free(copy);
   e->last_mouse_down_counter++;
   _evas_post_event_callback_call(eo_e, e, event_id);
   /* update touch point's state to EVAS_TOUCH_POINT_STILL */
   _evas_touch_point_update(eo_e, 0, pdata->seat->x, pdata->seat->y, EVAS_TOUCH_POINT_STILL);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_post_up_handle(Evas_Public_Data *e, Efl_Input_Pointer *parent_ev,
                Evas_Pointer_Data *pdata)
{
   Eina_List *l, *copy, *ins, *ll;
   Efl_Input_Pointer_Data *ev;
   Efl_Input_Pointer *evt;
   Evas_Object *eo_obj;
   Evas *eo_e = e->evas;
   Evas_Object_Pointer_Data *obj_pdata;
   int event_id;

   /* Duplicating UP event */
   evt = efl_duplicate(parent_ev);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   event_id = _evas_object_event_new();

   /* Actually we want an OUT */
   ev->action = EFL_POINTER_ACTION_OUT;

   /* get new list of ins */
   ins = evas_event_objects_event_list(eo_e, NULL, pdata->seat->x, pdata->seat->y);
   /* go thru old list of in objects */
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, ll, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if ((!eina_list_data_find(ins, eo_obj)) || (!pdata->seat->inside))
          {
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if (!obj_pdata->mouse_in) continue;
             _evas_event_mouse_in_set(pdata->seat, obj, 0);
             if (!e->is_frozen)
               {
                  ev->cur.x = pdata->seat->x;
                  ev->cur.y = pdata->seat->y;
                  ev->prev.x = pdata->seat->prev.x;
                  ev->prev.y = pdata->seat->prev.y;

                  _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_mouse_out_events(eo_obj, eo_e, evt, event_id);
                  if (e->delete_me) break;
               }
          }
     }
   _evas_post_event_callback_call(eo_e, e, event_id);

   eina_list_free(copy);

   if (pdata->seat->inside)
     {
        Evas_Object *eo_obj_itr;

        event_id = _evas_object_event_new();
        ev->action = EFL_POINTER_ACTION_IN;

        EINA_LIST_FOREACH(ins, l, eo_obj_itr)
          {
             Evas_Object_Protected_Data *obj_itr = efl_data_scope_get(eo_obj_itr, EFL_CANVAS_OBJECT_CLASS);
             if (!eina_list_data_find(pdata->seat->object.in, eo_obj_itr))
               {
                  obj_pdata = _evas_object_pointer_data_get(pdata, obj_itr);
                  if (!obj_pdata)
                    {
                       ERR("Could not find the object pointer data for device %p",
                           ev->device);
                       continue;
                    }
                  if (obj_pdata->mouse_in) continue;
                  _evas_event_mouse_in_set(pdata->seat, obj_itr, 1);
                  if (e->is_frozen) continue;

                  ev->cur.x = pdata->seat->x;
                  ev->cur.y = pdata->seat->y;
                  ev->prev.x = pdata->seat->prev.x;
                  ev->prev.y = pdata->seat->prev.y;

                  _evas_event_havemap_adjust_f(obj_itr, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  ev->action = EFL_POINTER_ACTION_IN;
                  evas_object_event_callback_call(eo_obj_itr, obj_itr, EVAS_CALLBACK_MOUSE_IN, evt,
                                                  event_id, EFL_EVENT_POINTER_IN);
                  if ((pdata->seat->x != pdata->seat->prev.x) && (pdata->seat->y != pdata->seat->prev.y))
                    {
                       ev->action = EFL_POINTER_ACTION_MOVE;
                       evas_object_event_callback_call(eo_obj_itr, obj_itr, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                       event_id, EFL_EVENT_POINTER_MOVE);
                    }
                  if ((obj_itr->proxy->is_proxy) &&
                      (obj_itr->proxy->src_events))
                    _evas_event_source_mouse_in_events(eo_obj_itr, eo_e, evt, event_id);
                  if (e->delete_me) break;
               }
          }
        _evas_post_event_callback_call(eo_e, e, event_id);
     }
   else
     {
        ins = eina_list_free(ins);
     }

   if (pdata->seat->mouse_grabbed == 0)
     {
        /* free our old list of ins */
        eina_list_free(pdata->seat->object.in);
        /* and set up the new one */
        pdata->seat->object.in = ins;
     }
   else
     {
        /* free our cur ins */
        eina_list_free(ins);
     }
   if (pdata->seat->inside)
     _evas_canvas_event_pointer_move_event_dispatch(e, pdata, ev->data);

   efl_unref(evt);
}

static void
_canvas_event_feed_mouse_up_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id = 0, b;
   Evas *eo_e;
   Evas_Pointer_Data *pdata;
   Eina_Bool cancel;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   b = ev->button;
   cancel = ev->action == EFL_POINTER_ACTION_CANCEL;
   if (cancel)
     DBG("ButtonEvent:cancel time=%u x=%d y=%d button=%d downs=%d",
         ev->timestamp, pdata->seat->x, pdata->seat->y, b, pdata->seat->downs);
   else
     DBG("ButtonEvent:up time=%u x=%d y=%d button=%d downs=%d",
         ev->timestamp, pdata->seat->x, pdata->seat->y, b, pdata->seat->downs);
   if ((b < 1) || (b > 32)) return;
   if (pdata->seat->downs <= 0) return;

   if (!cancel)
     {
        pdata->button &= ~(1u << (b - 1));
        pdata->seat->downs--;
     }

   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;
   eo_e = e->evas;
   evt = ev->eo;

   if (!cancel)
     event_id = _evas_object_event_new();

   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->touch_id = 0;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   if (!cancel)
     /* update released touch point */
     _evas_touch_point_update(eo_e, 0, pdata->seat->x, pdata->seat->y, EVAS_TOUCH_POINT_UP);
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Pointer_Mode pointer_mode;
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);

        if (obj->delete_me) continue;
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if ((!cancel) && ((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
             (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)) &&
            (obj_pdata->mouse_grabbed > 0))
          {
             obj_pdata->mouse_grabbed--;
             pdata->seat->mouse_grabbed--;
          }
        pointer_mode = obj_pdata->pointer_mode;
        if ((!e->is_frozen) &&
            (!evas_event_freezes_through(eo_obj, obj)))
          {
             ev->cur.x = pdata->seat->x;
             ev->cur.y = pdata->seat->y;
             ev->prev.x = pdata->seat->prev.x;
             ev->prev.y = pdata->seat->prev.y;

             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if (cancel)
               efl_event_callback_call(eo_obj, EFL_EVENT_POINTER_CANCEL, evt);
             else
               evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_UP, evt,
                                               event_id, EFL_EVENT_POINTER_UP);
             if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
               _evas_event_source_mouse_up_events(eo_obj, eo_e, evt, pdata, event_id, cancel);
             if (e->delete_me) break;
          }
        else if (evas_event_freezes_through(eo_obj, obj) &&
                 (obj->proxy->is_proxy) && (obj->proxy->src_events))
          {
             Evas_Object *eo_src = _evas_object_image_source_get(eo_obj);
             Evas_Object_Protected_Data *src = efl_data_scope_get(eo_src, EFL_CANVAS_OBJECT_CLASS);
             Evas_Object_Protected_Data *child;
             Evas_Object *eo_child;
             Eina_List *copy_events;

             if (src->delete_me) continue;
             copy_events = evas_event_list_copy(src->proxy->src_event_in);
             EINA_LIST_FREE(copy_events, eo_child)
               {
                  Evas_Object_Pointer_Data *obj_pdata;

                  child = efl_data_scope_get(eo_child, EFL_CANVAS_OBJECT_CLASS);
                  obj_pdata = _evas_object_pointer_data_get(pdata, child);
                  if (!obj_pdata)
                    {
                       ERR("Could not find the object pointer data for device %p",
                           ev->device);
                       continue;
                    }
                  if (((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
                       (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)) &&
                      (obj_pdata->mouse_grabbed > 0))
                    {
                       obj_pdata->mouse_grabbed--;
                       pdata->seat->mouse_grabbed--;
                    }
                }
          }
        if (pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
          {
             if ((!cancel) && (pdata->seat->nogrep > 0)) pdata->seat->nogrep--;
             break;
          }
     }
   eina_list_free(copy);
   if (!cancel)
     {
        e->last_mouse_up_counter++;
        _evas_post_event_callback_call(eo_e, e, event_id);

        if (pdata->seat->mouse_grabbed == 0)
          _post_up_handle(e, evt, pdata);

        if (pdata->seat->mouse_grabbed < 0)
          {
             ERR("BUG? pdata->seat->mouse_grabbed (=%d) < 0!",
                 pdata->seat->mouse_grabbed);
          }
     }
   /* remove released touch point from the touch point list */
   _evas_touch_point_remove(eo_e, 0);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_mouse_updown(Eo *eo_e, int b, Evas_Button_Flags flags,
                                unsigned int timestamp, const void *data,
                                Eina_Bool down, Efl_Input_Device *device, Eina_Bool cancel)
{
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;
   Evas_Public_Data *e;

   e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   if (!e) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!ev) return;

   ev->data = (void *) data;
   ev->timestamp = timestamp;
   ev->device = efl_ref(device ? device : _evas_event_legacy_device_get(eo_e, EINA_TRUE));
   if (cancel)
     ev->action = EFL_POINTER_ACTION_CANCEL;
   else
     ev->action = down ? EFL_POINTER_ACTION_DOWN : EFL_POINTER_ACTION_UP;
   ev->button = b;
   ev->button_flags = (Efl_Pointer_Flags)flags;
   ev->radius = 1;
   ev->radius_x = 1;
   ev->radius_y = 1;
   ev->pressure = 1;
   ev->angle = 0;
   //ev->window_pos = ?;
   //ev->fake = 1;

   /* first, send the cancel action through to trigger POINTER_CANCEL on all
    * relevant objects.
    * this does not change canvas state in any way.
    * note that the 'down' branch can only occur if 'cancel' is not true
    */
   if (down)
     _canvas_event_feed_mouse_down_internal(e, ev);
   else
     _canvas_event_feed_mouse_up_internal(e, ev);

   efl_unref(evt);

   /* next, emit actual up event and perform state changes */
   if (cancel)
     _canvas_event_feed_mouse_updown(eo_e, b, flags, timestamp, data, down, device, EINA_FALSE);
}

static void
_canvas_event_feed_mouse_updown_legacy(Eo *eo_e, int b, Evas_Button_Flags flags,
                                       unsigned int timestamp, const void *data,
                                       Eina_Bool down)
{
   _canvas_event_feed_mouse_updown(eo_e, b, flags, timestamp, data, down, NULL, EINA_FALSE);
}

EVAS_API void
evas_event_feed_mouse_down(Eo *eo_e, int b, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   _canvas_event_feed_mouse_updown_legacy(eo_e, b, flags, timestamp, data, 1);
}

EVAS_API void
evas_event_feed_mouse_up(Eo *eo_e, int b, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   _canvas_event_feed_mouse_updown_legacy(eo_e, b, flags, timestamp, data, 0);
}

static void
_canvas_event_feed_mouse_cancel_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Evas_Coord_Touch_Point *point;
   Efl_Input_Pointer_Data save;
   Eina_List *l, *ll;
   Evas_Event_Flags flags;
   Evas *eo_e;
   int i;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;

   save = *ev;
   eo_e = e->evas;
   _evas_walk(e);

   flags = evas_event_default_flags_get(eo_e);
   evas_event_default_flags_set(eo_e, (flags | EVAS_EVENT_FLAG_ON_HOLD));

   for (i = 0; i < 32; i++)
     {
        if ((pdata->button & (1u << i)))
          _canvas_event_feed_mouse_updown(eo_e, i + 1, 0, ev->timestamp, ev->data, 0, ev->device, EINA_TRUE);
     }

   ev->action = EFL_POINTER_ACTION_CANCEL;
   ev->value_flags |= value_flags;
   ev->event_flags = (Efl_Input_Flags)flags;
   EINA_LIST_FOREACH_SAFE(e->touch_points, l, ll, point)
     {
        if ((point->state == EVAS_TOUCH_POINT_DOWN) ||
            (point->state == EVAS_TOUCH_POINT_MOVE) ||
            (point->state == EVAS_TOUCH_POINT_STILL))
          {
             ev->touch_id = point->id;
             ev->cur.x = point->x;
             ev->cur.y = point->y;
             ev->prev = ev->cur;
             _canvas_event_feed_multi_up_internal(e, ev);
          }
     }
   evas_event_default_flags_set(eo_e, flags);
   _evas_unwalk(e);

   *ev = save;
}

EVAS_API void
evas_event_feed_mouse_cancel(Eo *eo_e, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   ev->timestamp = timestamp;
   ev->data = (void *) data;
   ev->device = efl_ref(_evas_event_legacy_device_get(e->evas, EINA_TRUE));

   _canvas_event_feed_mouse_cancel_internal(e, ev);

   efl_unref(evt);
}

static void
_canvas_event_feed_mouse_wheel_internal(Eo *eo_e, Efl_Input_Pointer_Data *pe)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id = 0;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_WHEEL_DELTA) |
         _efl_input_value_mask(EFL_INPUT_VALUE_WHEEL_HORIZONTAL);

   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, pe->device);
   if (!pdata) return;
   e->last_timestamp = pe->timestamp;

   event_id = _evas_object_event_new();

   evt = efl_duplicate(pe->eo);
   ev = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!ev) return;

   // adjust missing data based on evas state
   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->action = EFL_POINTER_ACTION_WHEEL;
   ev->value_flags |= value_flags;

   _evas_walk(e);
   copy = evas_event_list_copy(pdata->seat->object.in);

   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if (!evas_event_freezes_through(eo_obj, obj))
          {
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             ev->cur.x = pdata->seat->x;
             ev->cur.y = pdata->seat->y;
             ev->prev.x = pdata->seat->prev.x;
             ev->prev.y = pdata->seat->prev.y;

             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_WHEEL, evt,
                                             event_id, EFL_EVENT_POINTER_WHEEL);
             if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
               _evas_event_source_wheel_events(eo_obj, eo_e, evt, event_id);
             if (e->delete_me || e->is_frozen) break;
          }
     }
   eina_list_free(copy);
   _evas_post_event_callback_call(eo_e, e, event_id);

   efl_unref(evt);
   _evas_unwalk(e);
}

EVAS_API void
evas_event_feed_mouse_wheel(Eo *eo_e, int direction, int z, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);

   if (!ev) return;

   ev->wheel.horizontal = !!direction;
   ev->wheel.z = z;
   ev->timestamp = timestamp;
   ev->data = (void *) data;
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE));

   _canvas_event_feed_mouse_wheel_internal(eo_e, ev);
   efl_unref(evt);
}

static void
_canvas_event_feed_mouse_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Evas_Object *nogrep_obj = NULL;
   Evas_Object_Protected_Data *obj;
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   Eina_Vector2 cur_pt, prev_pt;
   Evas *eo_e;
   int event_id;
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   // inform which values are valid
   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_PREVIOUS_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_PREVIOUS_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_DX) |
         _efl_input_value_mask(EFL_INPUT_VALUE_DY) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTONS_PRESSED) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   if (ev->device)
     {
        pdata = _evas_pointer_data_by_device_get(e, ev->device);
        if (!pdata) return;
     }
   else
     {
        Evas_Pointer_Seat *pseat;
        if (!e->seats) return;
        pseat = EINA_INLIST_CONTAINER_GET(e->seats, Evas_Pointer_Seat);
        pseat->inside = 1;
        e->last_timestamp = ev->timestamp;
        pseat->prev.x = pseat->x;
        pseat->prev.y = pseat->y;

        // new pos
        pseat->x = ev->cur.x;
        pseat->y = ev->cur.y;
        return;
     }

   eo_e = e->evas;
   e->last_timestamp = ev->timestamp;

   ev->prev.x = pdata->seat->x;
   ev->prev.y = pdata->seat->y;

   // prev pos
   prev_pt.x = pdata->seat->prev.x = ev->prev.x;
   prev_pt.y = pdata->seat->prev.y = ev->prev.y;

   // new pos
   cur_pt.x = pdata->seat->x = ev->cur.x;
   cur_pt.y = pdata->seat->y = ev->cur.y;

   if ((!pdata->seat->inside) && (pdata->seat->mouse_grabbed == 0)) return;

   evt = ev->eo;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->pressed_buttons = pdata->button;
   ev->touch_id = 0;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   /* update moved touch point */
   if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
     _evas_touch_point_update(eo_e, 0, pdata->seat->x, pdata->seat->y, EVAS_TOUCH_POINT_MOVE);
   /* if our mouse button is grabbed to any objects */
   if (pdata->seat->mouse_grabbed > 0)
     {
        Eina_List *outs = NULL;

        /* Send normal mouse move events */
        ev->action = EFL_POINTER_ACTION_MOVE;

        event_id = _evas_object_event_new();

        /* go thru old list of in objects */
        copy = evas_event_list_copy(pdata->seat->object.in);
        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             Eina_Bool check_nogrep = EINA_FALSE;

             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if ((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN) &&
                 (pdata->seat->nogrep > 0))
               check_nogrep = EINA_TRUE;
             if ((!e->is_frozen) &&
                 _evas_event_object_pointer_allow(eo_obj, obj, obj_pdata) &&
                 (!evas_object_is_source_invisible(eo_obj, obj) ||
                  obj_pdata->mouse_grabbed))
               {
                  ev->cur = cur_pt;
                  ev->prev = prev_pt;

                  if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                    {
                       _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                       evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                       event_id, EFL_EVENT_POINTER_MOVE);
                       if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                         _evas_event_source_mouse_move_events(eo_obj, eo_e, evt, pdata, event_id);
                    }
               }
             else
                outs = eina_list_append(outs, eo_obj);
             if (check_nogrep)
               {
                  eina_list_free(copy);
                  eina_list_free(outs);
                  nogrep_obj = eo_obj;
                  goto nogrep;
               }
             if (e->delete_me) break;
          }
        eina_list_free(copy);
        _evas_post_event_callback_call(eo_e, e, event_id);


        /* Send mouse out events */
        ev->action = EFL_POINTER_ACTION_OUT;

        event_id = _evas_object_event_new();

        EINA_LIST_FREE(outs, eo_obj)
          {
             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if ((obj_pdata->mouse_grabbed == 0) && (!e->delete_me))
               {
                  if (!obj_pdata->mouse_in) continue;
                  _evas_event_mouse_in_set(pdata->seat, obj, 0);
                  if (obj->delete_me || e->is_frozen) continue;
                  pdata->seat->object.in = eina_list_remove(pdata->seat->object.in, eo_obj);

                  ev->cur = cur_pt;
                  ev->prev = prev_pt;

                  _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_mouse_out_events(eo_obj, eo_e, evt, event_id);
               }
          }
        _evas_post_event_callback_call(eo_e, e, event_id);
     }
   else
     {
        Eina_List *ins;

        event_id = _evas_object_event_new();

        /* get all new in objects */
        ins = evas_event_objects_event_list(eo_e, NULL, cur_pt.x, cur_pt.y);
        /* go thru old list of in objects */
        copy = evas_event_list_copy(pdata->seat->object.in);
        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             if (!obj) continue;
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }

             /* if its under the pointer and its visible and its in the new */
             /* in list */
             // FIXME: i don't think we need this
             //	     evas_object_clip_recalc(eo_obj);

             ev->cur = cur_pt;
             ev->prev = prev_pt;

             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if ((!e->is_frozen) &&
                 (evas_object_is_in_output_rect(eo_obj, obj, ev->cur.x, ev->cur.y, 1, 1)) &&
                 _evas_event_object_pointer_allow(eo_obj, obj, obj_pdata) &&
                 _evas_event_object_pointer_allow_precise(eo_obj, obj, ev->cur.x, ev->cur.y, ins) &&
                 (obj_pdata->mouse_grabbed || !evas_object_is_source_invisible(eo_obj, obj)))
               {
                  if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                    {
                       ev->action = EFL_POINTER_ACTION_MOVE;
                       evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                       event_id, EFL_EVENT_POINTER_MOVE);
                       if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                         _evas_event_source_mouse_move_events(eo_obj, eo_e, evt, pdata, event_id);
                    }
               }
             /* otherwise it has left the object */
             else if (obj_pdata->mouse_in)
               {
                  _evas_event_mouse_in_set(pdata->seat, obj, 0);
                  if (e->is_frozen) continue;
                  ev->action = EFL_POINTER_ACTION_OUT;
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_mouse_out_events(eo_obj, eo_e, evt, event_id);
                  if (e->delete_me) break;
               }
          }
        eina_list_free(copy);
        _evas_post_event_callback_call(eo_e, e, event_id);

        /* new event id for mouse in */
        event_id = _evas_object_event_new();

        /* go thru our current list of ins */
        EINA_LIST_FOREACH(ins, l, eo_obj)
          {
             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             /* if its not in the old list of ins send an enter event */
             if (!eina_list_data_find(pdata->seat->object.in, eo_obj))
               {
                  obj_pdata = _evas_object_pointer_data_get(pdata, obj);
                  if (!obj_pdata)
                    {
                       ERR("Could not find the object pointer data for device %p",
                           ev->device);
                       continue;
                    }
                  if (!obj_pdata->mouse_in)
                    {
                       _evas_event_mouse_in_set(pdata->seat, obj, 1);
                       if (e->is_frozen) continue;

                       ev->cur = cur_pt;
                       ev->prev = prev_pt;

                       _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                       ev->action = EFL_POINTER_ACTION_IN;
                       evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_IN, evt,
                                                       event_id, EFL_EVENT_POINTER_IN);
                       if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                          {
                             ev->action = EFL_POINTER_ACTION_MOVE;
                             evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                             event_id, EFL_EVENT_POINTER_MOVE);
                          }
                       if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                         _evas_event_source_mouse_in_events(eo_obj, eo_e, evt, event_id);

                       if (e->delete_me) break;
                    }
               }
          }
        if (pdata->seat->mouse_grabbed == 0)
          {
             /* free our old list of ins */
             eina_list_free(pdata->seat->object.in);
             /* and set up the new one */
             pdata->seat->object.in = ins;
          }
        else
          {
             /* free our cur ins */
             eina_list_free(ins);
          }
        _evas_post_event_callback_call(eo_e, e, event_id);
     }

nogrep:
   if (nogrep_obj)
     {
        Eina_List *ins = NULL, *newin = NULL, *lst = NULL;
        Evas_Object *eo_below_obj;

        event_id = _evas_object_event_new();

        /* go thru old list of in objects */
        copy = evas_event_list_copy(pdata->seat->object.in);
        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             if (eo_obj == nogrep_obj)
               {
                  lst = l->next;
                  break;
               }
          }

        /* get all new in objects */
        eo_below_obj = evas_object_below_get(nogrep_obj);
        if (eo_below_obj)
          {
             Evas_Object_Protected_Data *below_obj = efl_data_scope_get(eo_below_obj, EFL_CANVAS_OBJECT_CLASS);
             int norep = 0;
             ins = _evas_event_object_list_raw_in_get(eo_e, NULL,
                                                      EINA_INLIST_GET(below_obj), NULL, NULL,
                                                      pdata->seat->x, pdata->seat->y,
                                                      &norep, EINA_FALSE, EINA_TRUE);
          }

        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             newin = eina_list_append(newin, eo_obj);
             if (eo_obj == nogrep_obj) break;
          }

        // NOTE: was foreach + append without free (smelled bad)
        newin = eina_list_merge(newin, ins);
        EINA_LIST_FOREACH(lst, l, eo_obj)
          {
             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             /* if its under the pointer and its visible and its in the new */
             /* in list */
             // FIXME: i don't think we need this
             //	     evas_object_clip_recalc(eo_obj);

             ev->cur = cur_pt;
             ev->prev = prev_pt;

             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if ((!e->is_frozen) &&
                 evas_object_is_in_output_rect(eo_obj, obj, ev->cur.x, ev->cur.y, 1, 1) &&
                 _evas_event_object_pointer_allow(eo_obj, obj, obj_pdata) &&
                 _evas_event_object_pointer_allow_precise(eo_obj, obj, ev->cur.x, ev->cur.y, newin) &&
                 (obj_pdata->mouse_grabbed || !evas_object_is_source_invisible(eo_obj, obj)))
               {
                  if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                    {
                       ev->action = EFL_POINTER_ACTION_MOVE;
                       evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                       event_id, EFL_EVENT_POINTER_MOVE);
                       if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                         _evas_event_source_mouse_move_events(eo_obj, eo_e, evt, pdata, event_id);
                    }
               }
             else
               {
                  /* otherwise it has left the object */
                  if (!obj_pdata->mouse_in) continue;
                  _evas_event_mouse_in_set(pdata->seat, obj, 0);
                  if (e->is_frozen) continue;
                  ev->action = EFL_POINTER_ACTION_OUT;
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                                  event_id, EFL_EVENT_POINTER_OUT);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_mouse_out_events(eo_obj, eo_e, evt, event_id);
               }
             if (e->delete_me) break;
          }
        eina_list_free(copy);
        _evas_post_event_callback_call(eo_e, e, event_id);

        event_id = _evas_object_event_new();

        /* go thru our current list of ins */
        EINA_LIST_FOREACH(newin, l, eo_obj)
          {
             obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             /* if its not in the old list of ins send an enter event */
             if (!eina_list_data_find(pdata->seat->object.in, eo_obj))
               {
                  obj_pdata = _evas_object_pointer_data_get(pdata, obj);
                  if (!obj_pdata)
                    {
                       ERR("Could not find the object pointer data for device %p",
                           ev->device);
                       continue;
                    }
                  if (obj_pdata->mouse_in) continue;
                  _evas_event_mouse_in_set(pdata->seat, obj, 1);
                  if (e->is_frozen) continue;

                  ev->cur = cur_pt;
                  ev->prev = prev_pt;

                  _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

                  ev->action = EFL_POINTER_ACTION_IN;
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_IN, evt,
                                                  event_id, EFL_EVENT_POINTER_IN);
                  if (!EINA_DBL_EQ(cur_pt.x, prev_pt.x) || !EINA_DBL_EQ(cur_pt.y, prev_pt.y))
                    {
                       ev->action = EFL_POINTER_ACTION_MOVE;
                       evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                       event_id, EFL_EVENT_POINTER_MOVE);
                    }
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_mouse_in_events(eo_obj, eo_e, evt, event_id);
                  if (e->delete_me) break;
               }
          }
        /* free our old list of ins */
        eina_list_free(pdata->seat->object.in);
        /* and set up the new one */
        pdata->seat->object.in = newin;

        _evas_post_event_callback_call(eo_e, e, event_id);
     }

   _evas_unwalk(e);
   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_mouse_move_legacy(Evas *eo_e, Evas_Public_Data *e, int x, int y,
                                     unsigned int timestamp, const void *data)
{
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!ev) return;

   ev->data = (void *) data;
   ev->timestamp = timestamp;
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE));
   ev->cur.x = x;
   ev->cur.y = y;

   _canvas_event_feed_mouse_move_internal(e, ev);

   efl_unref(evt);
}

EVAS_API void
evas_event_input_mouse_move(Eo *eo_e, int x, int y, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_mouse_move_legacy(eo_e, e, x - e->framespace.x, y - e->framespace.y, timestamp, data);
}

EVAS_API void
evas_event_feed_mouse_move(Eo *eo_e, int x, int y, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_mouse_move_legacy(eo_e, e, x, y, timestamp, data);
}

static void
_canvas_event_feed_mouse_in_internal(Evas *eo_e, Efl_Input_Pointer_Data *ev)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Pointer *evt;
   Eina_List *ins;
   Eina_List *l;
   Evas_Object *eo_obj;
   int event_id;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTONS_PRESSED) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   if (ev->device)
     {
        pdata = _evas_pointer_data_by_device_get(e, ev->device);
        if (!pdata) return;
     }
   else
     {
        Evas_Pointer_Seat *pseat;
        if (!e->seats) return;
        pseat = EINA_INLIST_CONTAINER_GET(e->seats, Evas_Pointer_Seat);
        pseat->inside = 1;
        return;
     }

   pdata->seat->inside = 1;
   if (e->is_frozen) return;

   e->last_timestamp = ev->timestamp;
   if (pdata->seat->mouse_grabbed != 0) return;

   evt = ev->eo;
   ev->action = EFL_POINTER_ACTION_IN;
   ev->pressed_buttons = pdata->button;
   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   event_id = _evas_object_event_new();

   _evas_walk(e);
   /* get new list of ins */
   ins = evas_event_objects_event_list(eo_e, NULL, pdata->seat->x, pdata->seat->y);
   EINA_LIST_FOREACH(ins, l, eo_obj)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if ((!obj) || (obj->delete_me)) continue;
        if (!eina_list_data_find(pdata->seat->object.in, eo_obj))
          {
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if (obj_pdata->mouse_in) continue;
             _evas_event_mouse_in_set(pdata->seat, obj, 1);

             ev->cur.x = pdata->seat->x;
             ev->cur.y = pdata->seat->y;
             ev->prev.x = pdata->seat->prev.x;
             ev->prev.y = pdata->seat->prev.y;
             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             ev->action = EFL_POINTER_ACTION_IN;
             efl_ref(eo_obj);
             evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_IN, evt,
                                             event_id, EFL_EVENT_POINTER_IN);
             if (!EINA_DBL_EQ(pdata->seat->x, ev->prev.x) &&
                 !EINA_DBL_EQ(pdata->seat->y, ev->prev.y))
               {
                  ev->action = EFL_POINTER_ACTION_MOVE;
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_MOVE, evt,
                                                  event_id, EFL_EVENT_POINTER_MOVE);
               }
             if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
               _evas_event_source_mouse_in_events(eo_obj, eo_e, evt, event_id);
             efl_unref(eo_obj);
             if (e->delete_me || e->is_frozen) break;
          }
     }
   /* free our old list of ins */
   eina_list_free(pdata->seat->object.in);
   /* and set up the new one */
   pdata->seat->object.in = ins;
   _evas_post_event_callback_call(eo_e, e, event_id);
   _canvas_event_feed_mouse_move_internal(e, ev);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_mouse_out_internal(Evas *eo_e, Efl_Input_Pointer_Data *ev)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTONS_PRESSED) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);
   if (ev->device)
     {
        pdata = _evas_pointer_data_by_device_get(e, ev->device);
        if (!pdata) return;
     }
   else
     {
        Evas_Pointer_Seat *pseat;
        if (!e->seats) return;
        pseat = EINA_INLIST_CONTAINER_GET(e->seats, Evas_Pointer_Seat);
        pseat->inside = 0;
        return;
     }
   pdata->seat->inside = 0;

   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;

   event_id = _evas_object_event_new();

   evt = ev->eo;
   ev->action = EFL_POINTER_ACTION_OUT;
   ev->pressed_buttons = pdata->button;
   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   /* if our mouse button is inside any objects */
   /* go thru old list of in objects */
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if ((!obj) || (obj->delete_me)) continue;
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if (!obj_pdata->mouse_in) continue;
        _evas_event_mouse_in_set(pdata->seat, obj, 0);
        efl_ref(eo_obj);

        ev->cur.x = pdata->seat->x;
        ev->cur.y = pdata->seat->y;
        ev->prev.x = pdata->seat->prev.x;
        ev->prev.y = pdata->seat->prev.y;
        _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MOUSE_OUT, evt,
                                        event_id, EFL_EVENT_POINTER_OUT);
        if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
          _evas_event_source_mouse_out_events(eo_obj, eo_e, evt, event_id);
        efl_unref(eo_obj);
        if (e->delete_me || e->is_frozen) break;
        obj_pdata->mouse_grabbed = 0;
     }
   eina_list_free(copy);

   /* free our old list of ins */
   pdata->seat->object.in =  eina_list_free(pdata->seat->object.in);
   pdata->seat->mouse_grabbed = 0;
   _evas_post_event_callback_call(eo_e, e, event_id);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_mouse_inout_legacy(Eo *eo_e, unsigned int timestamp,
                                      const void *data, Eina_Bool in)
{
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!ev) return;

   ev->timestamp = timestamp;
   ev->data = (void *) data;
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE));

   if (in)
     _canvas_event_feed_mouse_in_internal(eo_e, ev);
   else
     _canvas_event_feed_mouse_out_internal(eo_e, ev);

   efl_unref(evt);
}

EVAS_API void
evas_event_feed_mouse_in(Eo *eo_e, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   _canvas_event_feed_mouse_inout_legacy(eo_e, timestamp, data, EINA_TRUE);
}

EVAS_API void
evas_event_feed_mouse_out(Eo *eo_e, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   _canvas_event_feed_mouse_inout_legacy(eo_e, timestamp, data, EINA_FALSE);
}

static void
_canvas_event_feed_multi_down_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   Eina_Vector2 point;
   Evas *eo_e;
   int event_id;
   int addgrab = 0;
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID) |
         _efl_input_value_mask(EFL_INPUT_VALUE_BUTTON);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;
   eo_e = e->evas;
   DBG("ButtonEvent:multi down time=%u x=%.1f y=%.1f button=%d downs=%d",
       ev->timestamp, ev->cur.x, ev->cur.y, ev->touch_id, pdata->seat->downs);
   pdata->seat->downs++;
   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;

   event_id = _evas_object_event_new();

   point = ev->cur;
   evt = ev->eo;
   ev->action = EFL_POINTER_ACTION_DOWN;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   /* append new touch point to the touch point list */
   _evas_touch_point_append(eo_e, ev->touch_id, ev->cur.x, ev->cur.y);
   if (pdata->seat->mouse_grabbed == 0)
     {
        if (pdata->seat->downs > 1)
          addgrab = pdata->seat->downs - 1;
        else /* this is the first touch, which means it's also a move event, which means we need to redo this */
          {
             /* get all new in objects */
             Eina_List *ins = evas_event_objects_event_list(eo_e, NULL, ev->cur.x, ev->cur.y);
             /* free our old list of ins */
             eina_list_free(pdata->seat->object.in);
             /* and set up the new one */
             pdata->seat->object.in = ins;
          }
     }
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        if (obj_pdata->pointer_mode != EVAS_OBJECT_POINTER_MODE_NOGRAB)
          {
             obj_pdata->mouse_grabbed += addgrab + 1;
             pdata->seat->mouse_grabbed += addgrab + 1;
          }
     }
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        ev->cur = point;
        ev->prev.x = pdata->seat->prev.x;
        ev->prev.y = pdata->seat->prev.y;
        _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

        evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MULTI_DOWN, evt,
                                        event_id, EFL_EVENT_FINGER_DOWN);
        if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
          _evas_event_source_multi_down_events(obj, e, evt, pdata, event_id);
        if (e->delete_me || e->is_frozen) break;
     }
   eina_list_free(copy);

   _evas_post_event_callback_call(eo_e, e, event_id);
   /* update touch point's state to EVAS_TOUCH_POINT_STILL */
   _evas_touch_point_update(eo_e, ev->touch_id, ev->cur.x, ev->cur.y, EVAS_TOUCH_POINT_STILL);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_multi_up_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   Eina_Vector2 point;
   Evas *eo_e;
   int event_id;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;
   eo_e = e->evas;
   DBG("ButtonEvent:multi up time=%u x=%.1f y=%.1f device=%d downs=%d",
       ev->timestamp, ev->cur.x, ev->cur.y, ev->touch_id, pdata->seat->downs);
   if (pdata->seat->downs <= 0) return;
   pdata->seat->downs--;
   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;

   event_id = _evas_object_event_new();

   point = ev->cur;
   evt = ev->eo;
   ev->action = EFL_POINTER_ACTION_UP;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   /* update released touch point */
   _evas_touch_point_update(eo_e, ev->touch_id, ev->cur.x, ev->cur.y, EVAS_TOUCH_POINT_UP);
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Pointer_Data *obj_pdata;
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        obj_pdata = _evas_object_pointer_data_get(pdata, obj);
        if (!obj_pdata)
          {
             ERR("Could not find the object pointer data for device %p",
                 ev->device);
             continue;
          }
        ev->cur = point;
        ev->prev.x = pdata->seat->prev.x;
        ev->prev.y = pdata->seat->prev.y;

        _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);
        if (((obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_AUTOGRAB) ||
            (obj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)) &&
            (obj_pdata->mouse_grabbed > 0))
          {
             obj_pdata->mouse_grabbed--;
             pdata->seat->mouse_grabbed--;
          }
        evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MULTI_UP, evt,
                                        event_id, EFL_EVENT_FINGER_UP);
        if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
          _evas_event_source_multi_up_events(obj, e, evt, pdata, event_id);
        if (e->delete_me || e->is_frozen) break;
     }
   eina_list_free(copy);
   if (pdata->seat->mouse_grabbed == 0)
     {
        _post_up_handle(e, evt, pdata);
        _evas_post_event_callback_call(eo_e, e, event_id);
     }

   /* remove released touch point from the touch point list */
   _evas_touch_point_remove(eo_e, ev->touch_id);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static inline void
_canvas_event_feed_multi_internal(Evas *eo_e, Evas_Public_Data *e,
                                  int d, int x, int y,
                                  double rad, double radx, double rady,
                                  double pres, double ang,
                                  double fx, double fy,
                                  Evas_Button_Flags flags,
                                  unsigned int timestamp,
                                  const void *data, Efl_Pointer_Action action)
{
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   if (EINA_DBL_EQ(fx, 0.0)) fx = x;
   if (EINA_DBL_EQ(fy, 0.0)) fy = y;

   ev->action = action;
   ev->touch_id = d;
   ev->cur.x = fx;
   ev->cur.y = fy;
   ev->pressure = pres;
   ev->angle = ang;
   ev->radius = rad;
   ev->radius_x = radx;
   ev->radius_y = rady;
   ev->button_flags = (Efl_Pointer_Flags)flags;
   ev->timestamp = timestamp;
   ev->data = (void *) data;
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE));

   switch (action)
     {
      case EFL_POINTER_ACTION_DOWN:
        _canvas_event_feed_multi_down_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_UP:
        _canvas_event_feed_multi_up_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_MOVE:
        _canvas_event_feed_multi_move_internal(e, ev);
        break;

      default: break;
     }

   efl_unref(evt);
}

EVAS_API void
evas_event_input_multi_down(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d,
                                     x - e->framespace.x, y - e->framespace.y,
                                     rad, radx, rady, pres, ang,
                                     fx, fy, flags, timestamp, data, EFL_POINTER_ACTION_DOWN);
}

EVAS_API void
evas_event_feed_multi_down(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d, x, y, rad, radx, rady, pres, ang,
                                     fx, fy, flags, timestamp, data, EFL_POINTER_ACTION_DOWN);
}

EVAS_API void
evas_event_input_multi_up(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d,
                                     x - e->framespace.x, y - e->framespace.y,
                                     rad, radx, rady, pres, ang,
                                     fx, fy, flags, timestamp, data, EFL_POINTER_ACTION_UP);
}

EVAS_API void
evas_event_feed_multi_up(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, Evas_Button_Flags flags, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d, x, y, rad, radx, rady, pres, ang,
                                     fx, fy, flags, timestamp, data, EFL_POINTER_ACTION_UP);
}

static void
_canvas_event_feed_multi_move_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   Eina_Vector2 cur_pt, prev_pt;
   Evas *eo_e;
   int event_id;
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   /* FIXME: Add previous x,y information (from evas touch point list) */
   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP) |
         _efl_input_value_mask(EFL_INPUT_VALUE_X) |
         _efl_input_value_mask(EFL_INPUT_VALUE_Y) |
         _efl_input_value_mask(EFL_INPUT_VALUE_TOUCH_ID);

   if (!e || !ev) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;
   eo_e = e->evas;
   event_id = _evas_object_event_new();

   if (e->is_frozen) return;
   e->last_timestamp = ev->timestamp;

   if ((!pdata->seat->inside) && (pdata->seat->mouse_grabbed == 0)) return;

   evt = ev->eo;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->action = EFL_POINTER_ACTION_MOVE;
   ev->value_flags |= value_flags;
   if (ev->device) efl_ref(ev->device);

   cur_pt = ev->cur;
   prev_pt = ev->prev;

   _evas_walk(e);
   /* update moved touch point */
   _evas_touch_point_update(eo_e, ev->touch_id, ev->cur.x, ev->cur.y, EVAS_TOUCH_POINT_MOVE);
   /* if our mouse button is grabbed to any objects */
   if (pdata->seat->mouse_grabbed > 0)
     {
        /* go thru old list of in objects */
        copy = evas_event_list_copy(pdata->seat->object.in);
        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             if (_evas_event_object_pointer_allow(eo_obj, obj, obj_pdata) &&
                 (!evas_object_is_source_invisible(eo_obj, obj) ||
                  obj_pdata->mouse_grabbed))
               {
                  ev->cur = cur_pt;
                  ev->prev = prev_pt;
                  _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MULTI_MOVE, evt,
                                                  event_id, EFL_EVENT_FINGER_MOVE);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_multi_move_events(obj, e, evt, pdata, event_id);

                  if (e->delete_me || e->is_frozen) break;
               }
          }
        eina_list_free(copy);
        _evas_post_event_callback_call(eo_e, e, event_id);
     }
   else
     {
        Eina_List *ins;

        /* get all new in objects */
        ins = evas_event_objects_event_list(eo_e, NULL, ev->cur.x, ev->cur.y);
        /* go thru old list of in objects */
        copy = evas_event_list_copy(pdata->seat->object.in);
        EINA_LIST_FOREACH(copy, l, eo_obj)
          {
             Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
             obj_pdata = _evas_object_pointer_data_get(pdata, obj);
             if (!obj_pdata)
               {
                  ERR("Could not find the object pointer data for device %p",
                      ev->device);
                  continue;
               }
             /* if its under the pointer and its visible and its in the new */
             /* in list */
             // FIXME: i don't think we need this
             //	     evas_object_clip_recalc(eo_obj);

             ev->cur = cur_pt;
             ev->prev = prev_pt;

             _evas_event_havemap_adjust_f(obj, &ev->cur, &ev->prev, obj_pdata->mouse_grabbed);

             if (evas_object_is_in_output_rect(eo_obj, obj, ev->cur.x, ev->cur.y, 1, 1) &&
                 _evas_event_object_pointer_allow(eo_obj, obj, obj_pdata) &&
                 _evas_event_object_pointer_allow_precise(eo_obj, obj, ev->cur.x, ev->cur.y, ins) &&
                 (obj_pdata->mouse_grabbed || !evas_object_is_source_invisible(eo_obj, obj)))
               {
                  evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_MULTI_MOVE, evt,
                                                  event_id, EFL_EVENT_FINGER_MOVE);
                  if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
                    _evas_event_source_multi_move_events(obj, e, evt, pdata, event_id);
               }
             if (e->delete_me || e->is_frozen) break;
          }
        eina_list_free(copy);
        if (pdata->seat->mouse_grabbed == 0)
          {
             /* free our old list of ins */
             eina_list_free(pdata->seat->object.in);
             /* and set up the new one */
             pdata->seat->object.in = ins;
          }
        else
          {
             /* free our cur ins */
             eina_list_free(ins);
          }
        _evas_post_event_callback_call(eo_e, e, event_id);
     }
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

EVAS_API void
evas_event_input_multi_move(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d, x - e->framespace.x, y - e->framespace.y,
                                     rad, radx, rady,
                                     pres, ang, fx, fy, EVAS_BUTTON_NONE, timestamp, data,
                                     EFL_POINTER_ACTION_MOVE);
}

EVAS_API void
evas_event_feed_multi_move(Eo *eo_e, int d, int x, int y, double rad, double radx, double rady, double pres, double ang, double fx, double fy, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_multi_internal(eo_e, e, d, x, y, rad, radx, rady,
                                     pres, ang, fx, fy, EVAS_BUTTON_NONE, timestamp, data,
                                     EFL_POINTER_ACTION_MOVE);
}

static void
_key_event_dispatch(Evas_Public_Data *e, void *event_info,
                    Efl_Input_Device *device,
                    const Efl_Event_Description *efl_event_desc,
                    Evas_Callback_Type evas_event_type, int event_id)
{
   Eo *focused;

   if (!device)
     device = e->default_seat;
   else
     {
        const char *name = efl_name_get(device);

        device = efl_input_device_seat_get(device);
        if (!device)
          {
             ERR("Could not find the parent seat from device name '%s'. Using default seat instead", name);
             device = e->default_seat;
          }
     }

   focused = eina_hash_find(e->focused_objects, &device);

   if (!focused)
     return;

   Evas_Object_Protected_Data *focused_obj =
     efl_data_scope_get(focused, EFL_CANVAS_OBJECT_CLASS);

   if (!focused_obj)
     {
        WRN("No element focused");
        return;
     }

   if (!e->is_frozen && !evas_event_freezes_through(focused, focused_obj))
     {
        evas_object_event_callback_call(focused, focused_obj,
                                        evas_event_type, event_info,
                                        event_id, efl_event_desc);
     }
}

static void
_canvas_event_feed_key_down_internal(Evas_Public_Data *e, Efl_Input_Key_Data *ev)
{
   Eina_Bool exclusive = EINA_FALSE;
   Efl_Input_Pointer *evt;
   int event_id = 0;
   Eo *eo_e;

   if (!e || !ev) return;
   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   eo_e = e->evas;
   e->last_timestamp = ev->timestamp;
   _evas_walk(e);

   event_id = _evas_object_event_new();

   evt = ev->eo;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   if (ev->device) efl_ref(ev->device);

   if (e->grabs)
     {
        Eina_List *l;
        Evas_Key_Grab *g;
        Evas_Modifier_Mask *seat_mask, modifier_mask;
        Efl_Input_Device *seat = NULL;

        e->walking_grabs++;
        if (ev->device)
          seat = efl_input_device_seat_get(ev->device);
        seat_mask = eina_hash_find(e->modifiers.masks, &seat);
        modifier_mask = seat_mask ? *seat_mask : 0;
        EINA_LIST_FOREACH(e->grabs, l, g)
          {
             if (g->just_added)
               {
                  g->just_added = EINA_FALSE;
                  continue;
               }
             if (g->delete_me) continue;
             if (!g->object) continue;
             if (!g->is_active) continue;
             if (((modifier_mask & g->modifiers) ||
                  (g->modifiers == modifier_mask)) &&
                 (!strcmp(ev->keyname, g->keyname)))
               {
                  if (!(modifier_mask & g->not_modifiers))
                    {
                       Evas_Object_Protected_Data *object_obj = efl_data_scope_get(g->object, EFL_CANVAS_OBJECT_CLASS);
                       if (!e->is_frozen &&
                           !evas_event_freezes_through(g->object, object_obj))
                         {
                            evas_object_event_callback_call(g->object, object_obj, EVAS_CALLBACK_KEY_DOWN, evt,
                                                            event_id, EFL_EVENT_KEY_DOWN);
                         }
                       if (g->exclusive) exclusive = EINA_TRUE;
                    }
               }
             if (e->delete_me) break;
          }
        e->walking_grabs--;
        if (e->walking_grabs <= 0)
          {
             while (e->delete_grabs > 0)
               {
                  e->delete_grabs--;
                  for (l = e->grabs; l;)
                    {
                       g = eina_list_data_get(l);
                       l = eina_list_next(l);
                       if (g->delete_me)
                         {
                            Evas_Object_Protected_Data *g_object_obj = efl_data_scope_get(g->object, EFL_CANVAS_OBJECT_CLASS);
                            evas_key_grab_free(g->object, g_object_obj, g->keyname,
                                               g->modifiers, g->not_modifiers);
                         }
                    }
               }
          }
     }
   if (!exclusive)
     _key_event_dispatch(e, evt, ev->device, EFL_EVENT_KEY_DOWN,
                         EVAS_CALLBACK_KEY_DOWN, event_id);
   _evas_post_event_callback_call(eo_e, e, event_id);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_key_up_internal(Evas_Public_Data *e, Efl_Input_Key_Data *ev)
{
   Eina_Bool exclusive = EINA_FALSE;
   Efl_Input_Pointer *evt;
   int event_id = 0;
   Eo *eo_e;

   if (!e || !ev) return;
   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   eo_e = e->evas;
   e->last_timestamp = ev->timestamp;
   _evas_walk(e);

   event_id = _evas_object_event_new();

   evt = ev->eo;
   ev->modifiers = &(e->modifiers);
   ev->locks = &(e->locks);
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   if (ev->device) efl_ref(ev->device);

   if (e->grabs)
     {
        Eina_List *l;
        Evas_Key_Grab *g;
        Evas_Modifier_Mask *seat_mask, modifier_mask;
        Efl_Input_Device *seat = NULL;

        e->walking_grabs++;
        if (ev->device)
          seat = efl_input_device_seat_get(ev->device);
        seat_mask = eina_hash_find(e->modifiers.masks, &seat);
        modifier_mask = seat_mask ? *seat_mask : 0;
        EINA_LIST_FOREACH(e->grabs, l, g)
          {
             if (g->just_added)
               {
                  g->just_added = EINA_FALSE;
                  continue;
               }
             if (g->delete_me) continue;
             if (!g->object) continue;
             if (!g->is_active) continue;
             if (((modifier_mask & g->modifiers) ||
                  (g->modifiers == modifier_mask)) &&
                 (!(modifier_mask & g->not_modifiers)) &&
                 (!strcmp(ev->keyname, g->keyname)))
               {
                  Evas_Object_Protected_Data *object_obj = efl_data_scope_get(g->object, EFL_CANVAS_OBJECT_CLASS);
                  if (!e->is_frozen &&
                        !evas_event_freezes_through(g->object, object_obj))
                    {
                       evas_object_event_callback_call
                             (g->object, object_obj, EVAS_CALLBACK_KEY_UP, evt,
                              event_id, EFL_EVENT_KEY_UP);
                    }
                  if (g->exclusive) exclusive = EINA_TRUE;
               }
             if (e->delete_me) break;
          }
        e->walking_grabs--;
        if (e->walking_grabs <= 0)
          {
             while (e->delete_grabs > 0)
               {
                  Eina_List *ll, *l_next;
                  Evas_Key_Grab *gr;

                  e->delete_grabs--;
                  EINA_LIST_FOREACH_SAFE(e->grabs, ll, l_next, gr)
                    {
                       if (gr->delete_me)
                         {
                            Evas_Object_Protected_Data *gr_object_obj =
                               efl_data_scope_get(gr->object, EFL_CANVAS_OBJECT_CLASS);
                            evas_key_grab_free(gr->object, gr_object_obj, gr->keyname,
                                            gr->modifiers, gr->not_modifiers);
                         }
                    }
               }
          }
     }
   if (!exclusive)
     _key_event_dispatch(e, evt, ev->device, EFL_EVENT_KEY_UP,
                         EVAS_CALLBACK_KEY_UP, event_id);
   _evas_post_event_callback_call(eo_e, e, event_id);
   _evas_unwalk(e);

   if (ev->device) efl_unref(ev->device);
}

static void
_canvas_event_feed_key_legacy(Eo *eo_e, Evas_Public_Data *e,
                              const char *keyname, const char *key,
                              const char *string, const char *compose,
                              unsigned int timestamp, const void *data,
                              unsigned int keycode, Eina_Bool down)
{
   Efl_Input_Key_Data *ev = NULL;
   Efl_Input_Key *evt;

   if (!keyname) return;

   evt = efl_input_key_instance_get( eo_e, (void **) &ev);
   if (!ev) return;

   ev->keyname = (char *) keyname;
   ev->data = (void *) data;
   ev->key = key;
   ev->string = string;
   ev->compose = compose;
   ev->timestamp = timestamp;
   ev->keycode = keycode;
   ev->pressed = down;
   ev->no_stringshare = EINA_TRUE;
   ev->device = efl_ref(_evas_event_legacy_device_get(e->evas, EINA_FALSE));


   if (down)
     _canvas_event_feed_key_down_internal(e, ev);
   else
     _canvas_event_feed_key_up_internal(e, ev);

   efl_unref(evt);
}

EVAS_API void
evas_event_feed_key_down(Eo *eo_e, const char *keyname, const char *key, const char *string, const char *compose, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_key_legacy(eo_e, e, keyname, key, string,
                                 compose, timestamp, data, 0, 1);
}

EVAS_API void
evas_event_feed_key_up(Eo *eo_e, const char *keyname, const char *key, const char *string, const char *compose, unsigned int timestamp, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_key_legacy(eo_e, e, keyname, key, string,
                                 compose, timestamp, data, 0, 0);
}

EVAS_API void
evas_event_feed_key_down_with_keycode(Eo *eo_e, const char *keyname, const char *key, const char *string, const char *compose, unsigned int timestamp, const void *data, unsigned int keycode)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_key_legacy(eo_e, e, keyname, key, string,
                                 compose, timestamp, data, keycode, 1);
}

EVAS_API void
evas_event_feed_key_up_with_keycode(Eo *eo_e, const char *keyname, const char *key, const char *string, const char *compose, unsigned int timestamp, const void *data, unsigned int keycode)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   _canvas_event_feed_key_legacy(eo_e, e, keyname, key, string,
                                 compose, timestamp, data, keycode, 0);
}

EVAS_API void
evas_event_feed_hold(Eo *eo_e, int hold, unsigned int timestamp, const void *data)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Hold_Data *ev = NULL;
   Efl_Input_Hold *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id = 0;
   Evas_Pointer_Data *pdata;

   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);
   e->last_timestamp = timestamp;

   event_id = _evas_object_event_new();

   evt = efl_input_hold_instance_get(eo_e, (void **) &ev);
   if (!ev) return;

   ev->hold = !!hold;
   ev->data = (void *) data;
   ev->timestamp = timestamp;
   ev->event_flags = (Efl_Input_Flags)e->default_event_flags;
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE));

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;


   _evas_walk(e);
   copy = evas_event_list_copy(pdata->seat->object.in);
   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if ( !evas_event_freezes_through(eo_obj, obj))
          {
             evas_object_event_callback_call(eo_obj, obj, EVAS_CALLBACK_HOLD, evt,
                                             event_id, EFL_EVENT_HOLD);
             if ((obj->proxy->is_proxy) && (obj->proxy->src_events))
               _evas_event_source_hold_events(eo_obj, event_id, evt);
          }
        if (e->delete_me || e->is_frozen) break;
     }
   eina_list_free(copy);
   _evas_post_event_callback_call(eo_e, e, event_id);
   _evas_unwalk(e);
   _evas_object_event_new();

   efl_unref(evt);
}

void
_canvas_event_feed_axis_update_internal(Evas_Public_Data *e, Efl_Input_Pointer_Data *ev)
{
   Efl_Input_Pointer *evt;
   Eina_List *l, *copy;
   Evas_Object *eo_obj;
   int event_id = 0;
   Evas *eo_e;
   Evas_Pointer_Data *pdata;

   static const int value_flags =
         _efl_input_value_mask(EFL_INPUT_VALUE_TIMESTAMP);

   if (!e || !ev) return;
   if (e->is_frozen) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   pdata = _evas_pointer_data_by_device_get(e, ev->device);
   if (!pdata) return;
   eo_e = e->evas;
   e->last_timestamp = ev->timestamp;

   ev->action = EFL_POINTER_ACTION_AXIS;
   ev->value_flags |= value_flags;
   event_id = _evas_object_event_new();
   evt = ev->eo;

   if (ev->device) efl_ref(ev->device);

   _evas_walk(e);
   copy = evas_event_list_copy(pdata->seat->object.in);

   EINA_LIST_FOREACH(copy, l, eo_obj)
     {
        Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
        if (!evas_event_freezes_through(eo_obj, obj))
          {
             evas_object_event_callback_call(eo_obj, obj,
                                             EVAS_CALLBACK_AXIS_UPDATE, evt,
                                             event_id, EFL_EVENT_POINTER_AXIS);
             if (e->delete_me || e->is_frozen) break;
          }
     }
   eina_list_free(copy);
   _evas_post_event_callback_call(eo_e, e, event_id);

   _evas_unwalk(e);
   if (ev->device) efl_unref(ev->device);
}

EVAS_API void
evas_event_feed_axis_update(Evas *eo_e, unsigned int timestamp, int device, int toolid, int naxis, const Evas_Axis *axes, const void *data)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);
   Efl_Input_Pointer_Data *ev = NULL;
   Eina_Bool haswinx = 0, haswiny = 0;
   Efl_Input_Pointer *evt;
   double x = 0, y = 0;
   int n;

   if (!e) return;
   EVAS_EVENT_FEED_SAFETY_CHECK(e);

   evt = efl_input_pointer_instance_get( eo_e, (void **) &ev);
   if (!ev) return;

   ev->data = (void *) data;
   ev->timestamp = timestamp;
   ev->action = EFL_POINTER_ACTION_AXIS;
   ev->touch_id = toolid;

   // see also ecore_evas.c
   for (n = 0; n < naxis; n++)
     {
        const Evas_Axis *axis = &(axes[n]);
        switch (axis->label)
          {
           case EVAS_AXIS_LABEL_WINDOW_X:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_X);
             x = axis->value;
             haswinx = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_WINDOW_Y:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_Y);
             y = axis->value;
             haswiny = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_X:
             if (!haswinx)
               {
                  _efl_input_value_mark(ev, EFL_INPUT_VALUE_X);
                  x = axis->value;
               }
             ev->raw.x = axis->value;
             ev->has_raw = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_Y:
             if (!haswiny)
               {
                  _efl_input_value_mark(ev, EFL_INPUT_VALUE_Y);
                  y = axis->value;
               }
             ev->raw.y = axis->value;
             ev->has_raw = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_NORMAL_X:
             ev->norm.x = axis->value;
             ev->has_norm = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_NORMAL_Y:
             ev->norm.y = axis->value;
             ev->has_norm = EINA_TRUE;
             break;

           case EVAS_AXIS_LABEL_PRESSURE:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_PRESSURE);
             ev->pressure = axis->value;
             break;

           case EVAS_AXIS_LABEL_DISTANCE:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_DISTANCE);
             ev->distance = axis->value;
             break;

           case EVAS_AXIS_LABEL_AZIMUTH:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_AZIMUTH);
             ev->azimuth = axis->value;
             break;

           case EVAS_AXIS_LABEL_TILT:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_TILT);
             ev->tilt = axis->value;
             break;

           case EVAS_AXIS_LABEL_TWIST:
             _efl_input_value_mark(ev, EFL_INPUT_VALUE_TWIST);
             ev->twist = axis->value;
             break;

           case EVAS_AXIS_LABEL_UNKNOWN:
           case EVAS_AXIS_LABEL_TOUCH_WIDTH_MAJOR:
           case EVAS_AXIS_LABEL_TOUCH_WIDTH_MINOR:
           case EVAS_AXIS_LABEL_TOOL_WIDTH_MAJOR:
           case EVAS_AXIS_LABEL_TOOL_WIDTH_MINOR:
           default:
             DBG("Unsupported axis label %d, value %f (discarded)",
                 axis->label, axis->value);
             break;
          }
     }

   ev->cur.x = x;
   ev->cur.y = y;

   /* FIXME: set proper device based on the device id (X or WL specific) */
   ev->device = efl_ref(_evas_event_legacy_device_get(eo_e, EINA_TRUE)); // FIXME
   (void) device;

   _canvas_event_feed_axis_update_internal(e, ev);

   efl_unref(evt);
}

static void
_feed_mouse_move_eval_internal(Eo *eo_obj, Evas_Object_Protected_Data *obj)
{
   Evas_Public_Data *evas = obj->layer->evas;
   Eina_Bool in_output_rect;
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(evas, NULL);

   if (!pdata) return;
   in_output_rect = evas_object_is_in_output_rect(eo_obj, obj, pdata->seat->x,
                                                  pdata->seat->y, 1, 1);
   if ((in_output_rect) &&
       ((!obj->precise_is_inside) || (evas_object_is_inside(eo_obj, obj,
                                                            pdata->seat->x,
                                                            pdata->seat->y))))
     {
        _canvas_event_feed_mouse_move_legacy(evas->evas, evas,
                                             pdata->seat->x, pdata->seat->y,
                                             evas->last_timestamp, NULL);
     }
}
EOLIAN void
_efl_canvas_object_efl_object_event_freeze(Eo *obj, Evas_Object_Protected_Data *pd)
{
   efl_event_freeze(efl_super(obj, EFL_CANVAS_OBJECT_CLASS));
   if (efl_event_freeze_count_get(obj) == 1)
     {
        pd->freeze_events = EINA_TRUE;
        evas_object_smart_member_cache_invalidate(obj, EINA_FALSE, EINA_TRUE,
                                                  EINA_FALSE);
     }
}

EOLIAN void
_efl_canvas_object_efl_object_event_thaw(Eo *obj, Evas_Object_Protected_Data *pd)
{
   if (efl_event_freeze_count_get(obj) == 1)
     {
        pd->freeze_events = EINA_FALSE;
        evas_object_smart_member_cache_invalidate(obj, EINA_FALSE, EINA_TRUE,
                                                  EINA_FALSE);
        _feed_mouse_move_eval_internal(obj, pd);
     }
   efl_event_thaw(efl_super(obj, EFL_CANVAS_OBJECT_CLASS));
}

EVAS_API void
evas_object_freeze_events_set(Eo *eo_obj, Eina_Bool freeze)
{
   Evas_Object_Protected_Data *pd = EVAS_OBJECT_DATA_SAFE_GET(eo_obj);
   EINA_SAFETY_ON_NULL_RETURN(pd);

   freeze = !!freeze;
   if (pd->freeze_events == freeze) return;

   if (freeze)
     efl_event_freeze(eo_obj);
   else
     // The following check is needed, as eo does not accept more thaw calls than freeze calls.
     // However, evas legacy stuff accepted multiple flase sets
     if (efl_event_freeze_count_get(eo_obj) > 0)
       efl_event_thaw(eo_obj);
}

EVAS_API Eina_Bool
evas_object_freeze_events_get(const Eo *eo_obj EINA_UNUSED)
{
   return (efl_event_freeze_count_get(eo_obj) > 0);
}

EOLIAN void
_efl_canvas_object_pass_events_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Eina_Bool pass)
{
   pass = !!pass;
   if (obj->pass_events == pass) return;
   obj->pass_events = pass;
   evas_object_smart_member_cache_invalidate(eo_obj, EINA_TRUE, EINA_FALSE, EINA_FALSE);
   _feed_mouse_move_eval_internal(eo_obj, obj);
}

EOLIAN Eina_Bool
_efl_canvas_object_pass_events_get(const Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj)
{
   return obj->pass_events;
}

EOLIAN void
_efl_canvas_object_repeat_events_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Eina_Bool repeat)
{
   repeat = !!repeat;
   if (obj->repeat_events == repeat) return;
   obj->repeat_events = repeat;
   _feed_mouse_move_eval_internal(eo_obj, obj);
}

EOLIAN Eina_Bool
_efl_canvas_object_repeat_events_get(const Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj)
{
   return obj->repeat_events;
}

EOLIAN void
_efl_canvas_object_propagate_events_set(Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj, Eina_Bool prop)
{
   obj->no_propagate = !prop;
}

EOLIAN Eina_Bool
_efl_canvas_object_propagate_events_get(const Eo *eo_obj EINA_UNUSED, Evas_Object_Protected_Data *obj)
{
   return !(obj->no_propagate);
}

EOLIAN Eina_Bool
_efl_canvas_object_pointer_mode_by_device_set(Eo *eo_obj, Evas_Object_Protected_Data *obj, Efl_Input_Device *dev, Evas_Object_Pointer_Mode setting)
{
   int addgrab;
   Evas_Object *cobj;
   const Eina_List *l;
   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   pdata = _evas_pointer_data_by_device_get(obj->layer->evas, dev);
   if (!pdata) return EINA_FALSE;

   obj_pdata = _evas_object_pointer_data_get(pdata, obj);
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj_pdata, EINA_FALSE);

   /* ignore no-ops */
   if (obj_pdata->pointer_mode == setting) return EINA_FALSE;

   /* adjust by number of pointer down events */
   addgrab = pdata->seat->downs;
   switch (obj_pdata->pointer_mode)
     {
      /* nothing needed */
      case EVAS_OBJECT_POINTER_MODE_NOGRAB: break;
      /* decrement canvas nogrep (NO Grab/REPeat) counter */
      case EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN:
        if (obj_pdata->mouse_grabbed)
          pdata->seat->nogrep--;
        /* fall through */
      /* remove related grabs from canvas and object */
      case EVAS_OBJECT_POINTER_MODE_AUTOGRAB:
        if (obj_pdata->mouse_grabbed)
          {
             pdata->seat->mouse_grabbed -= obj_pdata->mouse_grabbed;
             obj_pdata->mouse_grabbed = 0;
          }
     }
   /* adjustments for new mode */
   switch (setting)
     {
      /* nothing needed */
      case EVAS_OBJECT_POINTER_MODE_NOGRAB: break;
      /* increment canvas nogrep (NO Grab/REPeat) counter */
      case EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN:
        pdata->seat->nogrep++;
        /* having nogrep set indicates that any object following it in
         * the pointer.object.in list will not be receiving events, meaning
         * that they will fail to unset any existing grabs/flags. unset them
         * now to avoid breaking the canvas
         */
        EINA_LIST_FOREACH(pdata->seat->object.in, l, cobj)
          {
             Evas_Object_Protected_Data *cobj_data;

             /* skip to the current object */
             if (cobj != eo_obj) continue;
             /* only change objects past it */
             EINA_LIST_FOREACH(l->next, l, cobj)
               {
                  Evas_Object_Pointer_Data *cobj_pdata;
                  cobj_data = efl_data_scope_get(cobj, EFL_CANVAS_OBJECT_CLASS);
                  cobj_pdata = _evas_object_pointer_data_get(pdata, cobj_data);
                  if (!cobj_pdata) continue;
                  if (!cobj_pdata->mouse_grabbed) continue;
                  cobj_pdata->mouse_grabbed -= addgrab;
                  pdata->seat->mouse_grabbed -= addgrab;
                  if (cobj_pdata->pointer_mode == EVAS_OBJECT_POINTER_MODE_NOGRAB_NO_REPEAT_UPDOWN)
                    pdata->seat->nogrep--;
               }
             break;
          }
        /* fall through */
      /* add all button grabs to this object */
      case EVAS_OBJECT_POINTER_MODE_AUTOGRAB:
        obj_pdata->mouse_grabbed += addgrab;
        pdata->seat->mouse_grabbed += addgrab;
     }
   obj_pdata->pointer_mode = setting;
   return EINA_TRUE;
}

EOLIAN Evas_Object_Pointer_Mode
_efl_canvas_object_pointer_mode_by_device_get(const Eo *eo_obj EINA_UNUSED,
                                              Evas_Object_Protected_Data *obj,
                                              Efl_Input_Device *dev)
{

   Evas_Pointer_Data *pdata;
   Evas_Object_Pointer_Data *obj_pdata;

   pdata = _evas_pointer_data_by_device_get(obj->layer->evas, dev);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pdata, EVAS_OBJECT_POINTER_MODE_AUTOGRAB);

   obj_pdata = _evas_object_pointer_data_get(pdata, obj);
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj_pdata, EVAS_OBJECT_POINTER_MODE_AUTOGRAB);
   return obj_pdata->pointer_mode;
}

EOLIAN Eina_Bool
_efl_canvas_object_pointer_mode_set(Eo *eo_obj, Evas_Object_Protected_Data *obj,
                                    Evas_Object_Pointer_Mode setting)
{
   return _efl_canvas_object_pointer_mode_by_device_set(eo_obj, obj, NULL, setting);
}

EOLIAN Evas_Object_Pointer_Mode
_efl_canvas_object_pointer_mode_get(const Eo *eo_obj, Evas_Object_Protected_Data *obj)
{
   return _efl_canvas_object_pointer_mode_by_device_get(eo_obj, obj, NULL);
}

EOLIAN Eina_Bool
_efl_canvas_object_efl_canvas_pointer_pointer_inside_get(const Eo *eo_obj,
                                                         Evas_Object_Protected_Data *obj,
                                                         Efl_Input_Device *pointer)
{
   Evas_Object_Protected_Data *in, *parent;
   Eo *eo_in, *eo_parent;
   Eina_List *l;
   Evas_Object_Pointer_Data *obj_pdata;
   Evas_Pointer_Data *pdata;

   EVAS_OBJECT_DATA_ALIVE_CHECK(obj, EINA_FALSE);

   if (!pointer)
     pointer = obj->layer->evas->default_mouse;

   if (!pointer) return EINA_FALSE;

   pdata = _evas_pointer_data_by_device_get(obj->layer->evas, pointer);
   if (!pdata) return EINA_FALSE;
   obj_pdata = _evas_object_pointer_data_get(pdata, obj);
   if (!obj_pdata) return EINA_FALSE;
   if (!obj->is_smart)
     return obj_pdata->mouse_in;

   /* This is to keep the legacy APIs evas_object_pointer_inside_by_device_get() &
    * evas_object_pointer_inside_get() old behaviour. */
   if (obj->is_pointer_inside_legacy) return EINA_FALSE;

   /* For smart objects, this is a bit expensive obj->mouse_in will not be set.
    * Alternatively we could count the number of in and out events propagated
    * to the smart object, assuming they always match. */
   EINA_LIST_FOREACH(pdata->seat->object.in, l, eo_in)
     {
        if (EINA_UNLIKELY(eo_in == eo_obj))
          return EINA_TRUE;

        in = EVAS_OBJECT_DATA_GET(eo_in);
        if (!EVAS_OBJECT_DATA_ALIVE(in)) continue;
        eo_parent = in->smart.parent;
        while (eo_parent)
          {
             if ((eo_parent == eo_obj) && !in->no_propagate)
               return EINA_TRUE;
             parent = EVAS_OBJECT_DATA_GET(eo_parent);
             if (!EVAS_OBJECT_DATA_ALIVE(parent)) break;
             eo_parent = parent->smart.parent;
          }
     }

   return EINA_FALSE;
}

EVAS_API void
evas_event_refeed_event(Eo *eo_e, void *event_copy, Evas_Callback_Type event_type)
{
   EINA_SAFETY_ON_FALSE_RETURN(efl_isa(eo_e, EVAS_CANVAS_CLASS));
   if (!event_copy) return;

   switch (event_type)
     {
      case EVAS_CALLBACK_MOUSE_IN:
          {
             Evas_Event_Mouse_In *ev = event_copy;
             evas_event_feed_mouse_in(eo_e, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MOUSE_OUT:
          {
             Evas_Event_Mouse_Out *ev = event_copy;
             evas_event_feed_mouse_out(eo_e, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MOUSE_DOWN:
          {
             Evas_Event_Mouse_Down *ev = event_copy;
             evas_event_feed_mouse_down(eo_e, ev->button, ev->flags, ev-> timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MOUSE_UP:
          {
             Evas_Event_Mouse_Up *ev = event_copy;
             evas_event_feed_mouse_up(eo_e, ev->button, ev->flags, ev-> timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MOUSE_MOVE:
          {
             Evas_Event_Mouse_Move *ev = event_copy;
             evas_event_feed_mouse_move(eo_e, ev->cur.canvas.x, ev->cur.canvas.y, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MOUSE_WHEEL:
          {
             Evas_Event_Mouse_Wheel *ev = event_copy;
             evas_event_feed_mouse_wheel(eo_e, ev->direction, ev-> z, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MULTI_DOWN:
          {
             Evas_Event_Multi_Down *ev = event_copy;
             evas_event_feed_multi_down(eo_e, ev->device, ev->canvas.x, ev->canvas.y, ev->radius, ev->radius_x, ev->radius_y, ev->pressure, ev->angle, ev->canvas.xsub, ev->canvas.ysub, ev->flags, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MULTI_UP:
          {
             Evas_Event_Multi_Up *ev = event_copy;
             evas_event_feed_multi_up(eo_e, ev->device, ev->canvas.x, ev->canvas.y, ev->radius, ev->radius_x, ev->radius_y, ev->pressure, ev->angle, ev->canvas.xsub, ev->canvas.ysub, ev->flags, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_MULTI_MOVE:
          {
             Evas_Event_Multi_Move *ev = event_copy;
             evas_event_feed_multi_move(eo_e, ev->device, ev->cur.canvas.x, ev->cur.canvas.y, ev->radius, ev->radius_x, ev->radius_y, ev->pressure, ev->angle, ev->cur.canvas.xsub, ev->cur.canvas.ysub, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_KEY_DOWN:
          {
             Evas_Event_Key_Down *ev = event_copy;
             evas_event_feed_key_down(eo_e, ev->keyname, ev->key, ev->string, ev->compose, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_KEY_UP:
          {
             Evas_Event_Key_Up *ev = event_copy;
             evas_event_feed_key_up(eo_e, ev->keyname, ev->key, ev->string, ev->compose, ev->timestamp, ev->data);
             break;
          }
      case EVAS_CALLBACK_AXIS_UPDATE:
          {
             Evas_Event_Axis_Update *ev = event_copy;
             evas_event_feed_axis_update(eo_e, ev->timestamp, ev->device, ev->toolid, ev->naxis, ev->axis, ev->data);
             break;
          }
      default: /* All non-input events are not handeled */
        break;
     }
}


EOLIAN int
_evas_canvas_event_down_count_by_device_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e,
                                            Efl_Input_Device *dev)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, dev);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pdata, 0);
   return pdata->seat->downs;
}

EOLIAN int
_evas_canvas_event_down_count_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return _evas_canvas_event_down_count_by_device_get(eo_e, e, NULL);
}

static void
_evas_canvas_event_pointer_cb(void *data, const Efl_Event *event)
{
   Efl_Input_Pointer_Data *ev = efl_data_scope_get(event->info, EFL_INPUT_POINTER_CLASS);
   Evas_Public_Data *e = data;
   Evas *eo_e = event->object;
   Eina_Bool nodev = 0;

   if (!ev) return;

   ev->evas_done = EINA_TRUE;
   ev->modifiers = &e->modifiers;
   ev->locks = &e->locks;

   if (!ev->device)
     {
        nodev = 1;
        ev->device = _evas_event_legacy_device_get(e->evas, EINA_TRUE);
     }

   switch (ev->action)
     {
      case EFL_POINTER_ACTION_MOVE:
        if (ev->touch_id == 0)
          _canvas_event_feed_mouse_move_internal(e, ev);
        else
          _canvas_event_feed_multi_move_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_DOWN:
        if (ev->touch_id == 0)
          _canvas_event_feed_mouse_down_internal(e, ev);
        else
          _canvas_event_feed_multi_down_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_UP:
        if (ev->touch_id == 0)
          _canvas_event_feed_mouse_up_internal(e, ev);
        else
          _canvas_event_feed_multi_up_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_CANCEL:
        _canvas_event_feed_mouse_cancel_internal(e, ev);
        break;

      case EFL_POINTER_ACTION_IN:
        _canvas_event_feed_mouse_in_internal(eo_e, ev);
        break;

      case EFL_POINTER_ACTION_OUT:
        _canvas_event_feed_mouse_out_internal(eo_e, ev);
        break;

      case EFL_POINTER_ACTION_WHEEL:
        _canvas_event_feed_mouse_wheel_internal(eo_e, ev);
        break;

      case EFL_POINTER_ACTION_AXIS:
        _canvas_event_feed_axis_update_internal(e, ev);
        break;

      default:
        ERR("unsupported event type: %d", ev->action);
        ev->evas_done = EINA_FALSE;
        break;
     }

   if (nodev) ev->device = NULL;
}

static void
_evas_canvas_event_key_cb(void *data, const Efl_Event *event)
{
   Efl_Input_Key *evt = event->info;
   Evas_Public_Data *e = data;
   Efl_Input_Key_Data *ev;
   Eina_Bool nodev = 0;

   ev = efl_data_scope_get(evt, EFL_INPUT_KEY_CLASS);
   if (!ev) return;

   if (!ev->device)
     {
        nodev = 1;
        ev->device = _evas_event_legacy_device_get(e->evas, EINA_FALSE);
     }

   ev->modifiers = &e->modifiers;
   ev->locks = &e->locks;

   if (ev->pressed)
     _canvas_event_feed_key_down_internal(e, ev);
   else
     _canvas_event_feed_key_up_internal(e, ev);

   if (nodev) ev->device = NULL;
   ev->evas_done = EINA_TRUE;
}

static void
_evas_canvas_event_focus_cb(void *data, const Efl_Event *event)
{
   Efl_Input_Device *seat = efl_canvas_scene_seat_default_get(event->object);
   Evas_Public_Data *e = data;

   EINA_SAFETY_ON_NULL_RETURN(seat);

   if (event->desc == EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN)
     {
        if (eina_list_data_find(e->focused_by, seat)) return;
        e->focused_by = eina_list_append(e->focused_by, seat);
     }
   else
     {
        if (!eina_list_data_find(e->focused_by, seat)) return;
        e->focused_by = eina_list_remove(e->focused_by, seat);
     }
}

// note: "hold" event comes from above (elm), not below (ecore)
EFL_CALLBACKS_ARRAY_DEFINE(_evas_canvas_event_pointer_callbacks,
{ EFL_EVENT_POINTER_MOVE, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_DOWN, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_UP, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_IN, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_OUT, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_CANCEL, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_WHEEL, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_POINTER_AXIS, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_FINGER_MOVE, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_FINGER_DOWN, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_FINGER_UP, _evas_canvas_event_pointer_cb },
{ EFL_EVENT_KEY_DOWN, _evas_canvas_event_key_cb },
{ EFL_EVENT_KEY_UP, _evas_canvas_event_key_cb },
{ EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN, _evas_canvas_event_focus_cb },
{ EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_OUT, _evas_canvas_event_focus_cb })

void
_evas_canvas_event_init(Evas *eo_e, Evas_Public_Data *e)
{
   efl_event_callback_array_add(eo_e, _evas_canvas_event_pointer_callbacks(), e);
}

void
_evas_canvas_event_shutdown(Evas *eo_e, Evas_Public_Data *e)
{
   efl_event_callback_array_del(eo_e, _evas_canvas_event_pointer_callbacks(), e);
}

void
_evas_canvas_event_pointer_move_event_dispatch(Evas_Public_Data *edata,
                                               Evas_Pointer_Data *pdata,
                                               void *data)
{
   Efl_Input_Pointer_Data *ev = NULL;
   Efl_Input_Pointer *evt;

   evt = efl_input_pointer_instance_get( edata->evas,
                                (void **) &ev);
   if (!evt) return;

   ev->data = (void *) data;
   ev->timestamp = edata->last_timestamp;
   ev->device = efl_ref(pdata->pointer);
   ev->cur.x = pdata->seat->x;
   ev->cur.y = pdata->seat->y;

   _canvas_event_feed_mouse_move_internal(edata, ev);

   efl_unref(evt);
}

void
_evas_canvas_event_pointer_in_rect_mouse_move_feed(Evas_Public_Data *edata,
                                                   Evas_Object *obj,
                                                   Evas_Object_Protected_Data *obj_data,
                                                   int w, int h,
                                                   Eina_Bool in_objects_list,
                                                   void *data)
{
   Evas_Pointer_Seat *pseat;

   EINA_INLIST_FOREACH(edata->seats, pseat)
     {
        if (!pseat->pointers) continue;
        if (!evas_object_is_in_output_rect(obj, obj_data, pseat->x,
                                           pseat->y, w, h))
          continue;
        if ((in_objects_list && eina_list_data_find(pseat->object.in, obj)) || !in_objects_list)
          {
             Evas_Pointer_Data *pdata = EINA_INLIST_CONTAINER_GET(pseat->pointers, Evas_Pointer_Data);
             _evas_canvas_event_pointer_move_event_dispatch(edata, pdata, data);
          }
     }
}

void
_evas_canvas_event_pointer_in_list_mouse_move_feed(Evas_Public_Data *edata,
                                                   Eina_List *was,
                                                   Evas_Object *obj,
                                                   Evas_Object_Protected_Data *obj_data,
                                                   int w, int h,
                                                   Eina_Bool xor_rule,
                                                   void *data)
{
   Evas_Pointer_Seat *pseat;

   EINA_INLIST_FOREACH(edata->seats, pseat)
     {
        Evas_Pointer_Data *pdata, *found = NULL;
        Eina_List *l;
        int in;

        if (!pseat->pointers) continue;
        in = evas_object_is_in_output_rect(obj, obj_data, pseat->x,
                                               pseat->y, w, h);
        EINA_LIST_FOREACH(was, l, pdata)
          if (pdata->seat == pseat)
            {
               found = pdata;
               break;
            }

        if ((xor_rule && ((in && !found) || (!in && found))) ||
            (!xor_rule && (in || found)))
          {
             if (!pdata) pdata = EINA_INLIST_CONTAINER_GET(pseat->pointers, Evas_Pointer_Data);
             _evas_canvas_event_pointer_move_event_dispatch(edata, pdata, data);
          }
     }
}
