#define EFL_INPUT_EVENT_PROTECTED

#include "evas_common_private.h"
#include "evas_private.h"
//#include "evas_cs.h"

#include "evas_image_private.h"
#include "evas_polygon_private.h"
#include "evas_vg_private.h"

#include "eo_internal.h"

#ifdef _WIN32
# include <evil_private.h> /* evil_init evil_shutdown */
#endif

#include <Ecore.h>

#define MY_CLASS EVAS_CANVAS_CLASS

#ifdef LKDEBUG
EAPI Eina_Bool lockdebug = EINA_FALSE;
EAPI int lockmax = 0;
#endif

static int _evas_init_count = 0;
int _evas_log_dom_global = -1;

EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_NONE = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_GENERIC = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_DOES_NOT_EXIST = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_PERMISSION_DENIED = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_CORRUPT_FILE = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_CANCELLED = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_INCOMPATIBLE_FILE = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_UNKNOWN_COLLECTION = 0;
EAPI Eina_Error EFL_GFX_IMAGE_LOAD_ERROR_RECURSIVE_REFERENCE = 0;

#define NUM_ERRORS 11

const char *efl_gfx_image_load_error_msgs[] = {
 "No error on load" ,
 "A non-specific error occurred" ,
 "File (or file path) does not exist" ,
 "Permission denied to an existing file (or path)" ,
 "Allocation of resources failure prevented load" ,
 "File corrupt (but was detected as a known format)" ,
 "File is not a known format" ,
 "Reading operation has been cancelled during decoding" ,
 "(Edje only) The file pointed to is incompatible, i.e., it doesn't match the library's current version's format." ,
 "(Edje only) The group/collection set to load from was not found in the file" ,
 "(Edje only) The group/collection set to load from had recursive references on its components"
};

static void
_efl_gfx_image_load_error_init(void)
{
   Eina_Error *table[] = {
     &EFL_GFX_IMAGE_LOAD_ERROR_NONE,
     &EFL_GFX_IMAGE_LOAD_ERROR_GENERIC,
     &EFL_GFX_IMAGE_LOAD_ERROR_DOES_NOT_EXIST,
     &EFL_GFX_IMAGE_LOAD_ERROR_PERMISSION_DENIED,
     &EFL_GFX_IMAGE_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED,
     &EFL_GFX_IMAGE_LOAD_ERROR_CORRUPT_FILE,
     &EFL_GFX_IMAGE_LOAD_ERROR_UNKNOWN_FORMAT,
     &EFL_GFX_IMAGE_LOAD_ERROR_CANCELLED,
     &EFL_GFX_IMAGE_LOAD_ERROR_INCOMPATIBLE_FILE,
     &EFL_GFX_IMAGE_LOAD_ERROR_UNKNOWN_COLLECTION,
     &EFL_GFX_IMAGE_LOAD_ERROR_RECURSIVE_REFERENCE
     };
   unsigned int i;

   if (EFL_GFX_IMAGE_LOAD_ERROR_GENERIC) return;
   /* skip EFL_GFX_IMAGE_LOAD_ERROR_NONE: this should always be 0 */
   for (i = 1; i < NUM_ERRORS; i++)
     *(table[i]) = eina_error_msg_static_register(efl_gfx_image_load_error_msgs[i]);
#undef TABLE_ENTRY
}

Eina_Error
_evas_load_error_to_efl_gfx_image_load_error(Evas_Load_Error err)
{
#define TABLE_ENTRY(NAME) [EVAS_LOAD_ERROR_##NAME] = &EFL_GFX_IMAGE_LOAD_ERROR_##NAME
   Eina_Error *table[] = {
     TABLE_ENTRY(NONE),
     TABLE_ENTRY(GENERIC),
     TABLE_ENTRY(DOES_NOT_EXIST),
     TABLE_ENTRY(PERMISSION_DENIED),
     TABLE_ENTRY(RESOURCE_ALLOCATION_FAILED),
     TABLE_ENTRY(CORRUPT_FILE),
     TABLE_ENTRY(UNKNOWN_FORMAT),
     TABLE_ENTRY(CANCELLED),
     //TABLE_ENTRY(INCOMPATIBLE_FILE),
     //TABLE_ENTRY(UNKNOWN_COLLECTION),
     //TABLE_ENTRY(RECURSIVE_REFERENCE)
     };
   if (err > EVAS_LOAD_ERROR_CANCELLED) return err;
   return *table[err];
#undef TABLE_ENTRY
}

Evas_Load_Error
_efl_gfx_image_load_error_to_evas_load_error(Eina_Error err)
{
    if (err && (err < EFL_GFX_IMAGE_LOAD_ERROR_GENERIC)) return EVAS_LOAD_ERROR_GENERIC;
#define CONVERT_ERR(NAME) if (err == EFL_GFX_IMAGE_LOAD_ERROR_##NAME) return EVAS_LOAD_ERROR_##NAME
   CONVERT_ERR(NONE);
   CONVERT_ERR(GENERIC);
   CONVERT_ERR(DOES_NOT_EXIST);
   CONVERT_ERR(PERMISSION_DENIED);
   CONVERT_ERR(RESOURCE_ALLOCATION_FAILED);
   CONVERT_ERR(CORRUPT_FILE);
   CONVERT_ERR(UNKNOWN_FORMAT);
   CONVERT_ERR(CANCELLED);
   //CONVERT_ERR(INCOMPATIBLE_FILE);
   //CONVERT_ERR(UNKNOWN_COLLECTION);
   //CONVERT_ERR(RECURSIVE_REFERENCE);
   return EVAS_LOAD_ERROR_GENERIC;
}

static Eina_Content*
_markup_to_utf8(Eina_Content *from, const char *to_type)
{
   Eina_Content *ret = NULL;
   Eina_Slice slice = eina_content_data_get(from);
   char *utf8 = evas_textblock_text_markup_to_utf8(NULL, slice.mem);
   if (utf8)
     {
        ret = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL(utf8), to_type);
        free(utf8);
     }
   return ret;
}

static Eina_Content*
_utf8_to_markup(Eina_Content *from, const char *to_type)
{
   Eina_Content *ret = NULL;
   Eina_Slice slice = eina_content_data_get(from);
   char *markup = evas_textblock_text_utf8_to_markup(NULL, slice.mem);
   if (markup)
     {
        ret = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL(markup), to_type);
        free(markup);
     }
   return ret;
}

EAPI int
evas_init(void)
{
   if (++_evas_init_count != 1)
     return _evas_init_count;

#ifdef LKDEBUG
   if (getenv("EVAS_LOCK_DEBUG"))
      {
         lockdebug = EINA_TRUE;
         lockmax = atoi(getenv("EVAS_LOCK_DEBUG"));
      }
#endif

#ifdef _WIN32
   if (!evil_init())
     return --_evas_init_count;
#endif

   EINA_SAFETY_ON_FALSE_GOTO(eina_init(), shutdown_evil);

   EINA_SAFETY_ON_FALSE_GOTO(eet_init(), shutdown_eet);
#ifdef BUILD_LOADER_EET
   /* additional init refcount; cannot fail */
   eet_init();
#endif

   _evas_log_dom_global = eina_log_domain_register
     ("evas_main", EVAS_DEFAULT_LOG_COLOR);
   if (_evas_log_dom_global < 0)
     {
        EINA_LOG_ERR("Can not create a module log domain.");
        goto shutdown_eina;
     }

   EINA_SAFETY_ON_FALSE_GOTO(efl_object_init(), shutdown_eo);

   EINA_SAFETY_ON_FALSE_GOTO(ecore_init(), shutdown_ecore);

   evas_module_init();
   EINA_SAFETY_ON_FALSE_GOTO(evas_async_events_init(), shutdown_module);
   _evas_preload_thread_init();
   evas_filter_init();
   evas_cache_vg_init();

   EINA_SAFETY_ON_FALSE_GOTO(evas_thread_init(), shutdown_filter);

   evas_common_init();

   eina_log_timing(_evas_log_dom_global,
		   EINA_LOG_STATE_STOP,
		   EINA_LOG_STATE_INIT);

   _efl_gfx_mapping_init();
   evas_focus_init();

   _efl_gfx_image_load_error_init();

   eina_content_converter_conversion_register("application/x-elementary-markup", "text/plain;charset=utf-8", _markup_to_utf8);
   eina_content_converter_conversion_register("text/plain;charset=utf-8", "application/x-elementary-markup", _utf8_to_markup);

   return _evas_init_count;

 shutdown_filter:
   evas_filter_shutdown();
   _evas_preload_thread_shutdown();
shutdown_module:
   evas_module_shutdown();
shutdown_ecore:
   efl_object_shutdown();
shutdown_eo:
   eina_log_domain_unregister(_evas_log_dom_global);
shutdown_eet:
#ifdef BUILD_LOADER_EET
   eet_shutdown();
#endif
   eet_shutdown();
shutdown_eina:
   eina_shutdown();
shutdown_evil:
#ifdef _WIN32
   evil_shutdown();
#endif

   return --_evas_init_count;
}

EAPI int
evas_shutdown(void)
{
   if (_evas_init_count <= 0)
     {
        EINA_LOG_ERR("Init count not greater than 0 in shutdown.");
        return 0;
     }
   if (--_evas_init_count != 0)
     return _evas_init_count;

   eina_log_timing(_evas_log_dom_global,
                   EINA_LOG_STATE_START,
                   EINA_LOG_STATE_SHUTDOWN);

   evas_focus_shutdown();

   evas_fonts_zero_free();

   evas_cache_vg_shutdown();

   evas_font_path_global_clear();

   evas_thread_shutdown();
   _evas_preload_thread_shutdown();
   evas_async_events_shutdown();

   ecore_shutdown();
   evas_common_shutdown();

   evas_filter_shutdown();
   evas_module_shutdown();

   _efl_gfx_mapping_shutdown();

   eina_cow_del(evas_object_proxy_cow);
   eina_cow_del(evas_object_map_cow);
   eina_cow_del(evas_object_state_cow);
   evas_object_state_cow = NULL;
   evas_object_map_cow = NULL;
   evas_object_proxy_cow = NULL;

   eina_cow_del(evas_object_image_pixels_cow);
   eina_cow_del(evas_object_image_load_opts_cow);
   eina_cow_del(evas_object_image_state_cow);
   evas_object_image_pixels_cow = NULL;
   evas_object_image_load_opts_cow = NULL;
   evas_object_image_state_cow = NULL;

   eina_cow_del(evas_object_mask_cow);
   evas_object_mask_cow = NULL;

#ifdef BUILD_LOADER_EET
   eet_shutdown();
#endif
   efl_object_shutdown();

   eina_log_domain_unregister(_evas_log_dom_global);

   eet_shutdown();

   eina_shutdown();
#ifdef _WIN32
   evil_shutdown();
#endif

   return _evas_init_count;
}


EAPI Evas *
evas_new(void)
{
   Evas_Object *eo_obj = efl_add(EVAS_CANVAS_CLASS, efl_main_loop_get());
   return eo_obj;
}

static void
_evas_key_mask_free(void *data)
{
   free(data);
}

static void
list_free(void *list)
{
   eina_list_free(list);
}

EOLIAN static Eo *
_evas_canvas_efl_object_constructor(Eo *eo_obj, Evas_Public_Data *e)
{
   eo_obj = efl_constructor(efl_super(eo_obj, MY_CLASS));

   e->evas = eo_obj;
   e->output.render_method = RENDER_METHOD_INVALID;
   e->viewport.w = 1;
   e->viewport.h = 1;
   e->framespace.x = 0;
   e->framespace.y = 0;
   e->framespace.w = 0;
   e->framespace.h = 0;
   e->hinting = EVAS_FONT_HINTING_BYTECODE;
   e->current_event = EVAS_CALLBACK_LAST;
   e->name_hash = eina_hash_string_superfast_new(list_free);
   eina_clist_init(&e->calc_list);
   eina_clist_init(&e->calc_done);

   efl_wref_add(efl_add(EFL_CANVAS_GESTURE_MANAGER_CLASS, eo_obj), &e->gesture_manager);
   e->gmd = efl_data_scope_get(e->gesture_manager, EFL_CANVAS_GESTURE_MANAGER_CLASS);

#define EVAS_ARRAY_SET(E, Array) \
   eina_array_step_set(&E->Array, sizeof (E->Array), \
                       ((1024 * sizeof (void*)) - sizeof (E->Array)) / sizeof (void*));

   EVAS_ARRAY_SET(e, delete_objects);
   EVAS_ARRAY_SET(e, restack_objects);
   EVAS_ARRAY_SET(e, render_objects);
   EVAS_ARRAY_SET(e, pending_objects);
   EVAS_ARRAY_SET(e, obscuring_objects);
   EVAS_ARRAY_SET(e, temporary_objects);
   EVAS_ARRAY_SET(e, snapshot_objects);
   EVAS_ARRAY_SET(e, clip_changes);
   EVAS_ARRAY_SET(e, scie_unref_queue);
   EVAS_ARRAY_SET(e, image_unref_queue);
   EVAS_ARRAY_SET(e, glyph_unref_queue);
   EVAS_ARRAY_SET(e, texts_unref_queue);

   eina_array_step_set(&e->render_post_change_objects, sizeof(e->render_post_change_objects), 10);
   eina_array_step_set(&e->map_clip_objects, sizeof(e->map_clip_objects), 64);

   e->active_objects.version = EINA_ARRAY_VERSION;
   eina_inarray_step_set(&e->active_objects,
                         sizeof(Eina_Inarray),
                         sizeof(Evas_Active_Entry),
                         256);

#undef EVAS_ARRAY_SET
   eina_lock_new(&(e->lock_objects));
   eina_spinlock_new(&(e->render.lock));
   eina_spinlock_new(&(e->post_render.lock));

   _evas_canvas_event_init(eo_obj, e);

   e->focused_objects = eina_hash_pointer_new(NULL);
   e->locks.masks = eina_hash_pointer_new(_evas_key_mask_free);
   e->modifiers.masks = eina_hash_pointer_new(_evas_key_mask_free);
   e->locks.e = e->modifiers.e = e;

   return eo_obj;
}

EAPI void
evas_free(Evas *eo_e)
{
   if (!eo_e) return;
   EVAS_TYPE_CHECK(eo_e);
   if (efl_parent_get(eo_e))
     efl_del(eo_e);
   else
     efl_unref(eo_e);
}

typedef struct _Forced_Death Forced_Death;
struct _Forced_Death
{
   Eina_Bool invalidated;
   Eina_Bool noref;
   Eina_Bool destroyed;
};

static void
_object_invalidate(void *data, const Efl_Event *ev EINA_UNUSED)
{
   Forced_Death *force = data;
   force->invalidated = EINA_TRUE;
}

static void
_object_del(void *data, const Efl_Event *ev EINA_UNUSED)
{
   Forced_Death *force = data;
   force->destroyed = EINA_TRUE;
}

static void
_object_noref(void *data, const Efl_Event *ev EINA_UNUSED)
{
   Forced_Death *force = data;
   force->noref = EINA_TRUE;
}

EFL_CALLBACKS_ARRAY_DEFINE(_object_forced_death,
                           { EFL_EVENT_DEL, _object_del },
                           { EFL_EVENT_INVALIDATE, _object_invalidate },
                           { EFL_EVENT_NOREF, _object_noref } );


EOLIAN static void
_evas_canvas_efl_object_invalidate(Eo *eo_e, Evas_Public_Data *e)
{
   Evas_Object_Protected_Data *o;
   Evas_Layer *lay;
   Eo *obj;
   Eina_Array stash = { 0 };

   evas_sync(eo_e);

   evas_canvas_async_block(e);
   evas_render_idle_flush(eo_e);

   efl_replace(&e->default_seat, NULL);
   efl_replace(&e->default_mouse, NULL);
   efl_replace(&e->default_keyboard, NULL);

   _evas_post_event_callback_free(eo_e);
   _evas_canvas_event_shutdown(eo_e, e);

   e->cleanup = 1;

   eina_array_step_set(&stash, sizeof (Eina_Array), 16);

   // The first pass should destroy all object that are properly referenced
   EINA_INLIST_FOREACH(e->layers, lay)
     {
        evas_layer_pre_free(lay);

        EINA_INLIST_FOREACH(lay->objects, o)
          {
             if (!o->delete_me)
               eina_array_push(&stash, o->object);
          }
     }

   while ((obj = eina_array_pop(&stash)))
     {
        Evas_Object_Protected_Data *pd = efl_data_scope_get(obj, EFL_CANVAS_OBJECT_CLASS);

        // Properly destroy depending on the object being legacy or not
        if (pd->legacy.ctor) evas_object_del(obj);
        else efl_del(obj);
     }

   // We are now potentially only facing zombies
   EINA_INLIST_FOREACH(e->layers, lay)
     {
        evas_layer_pre_free(lay);
        EINA_INLIST_FOREACH(lay->objects, o)
          {
             if (!o->delete_me)
               eina_array_push(&stash, o->object);
          }
     }

   // Killing zombies now
   while ((obj = eina_array_pop(&stash)))
     {
        Forced_Death force = {
          efl_invalidated_get(obj),
          efl_parent_get(obj) ? (efl_ref_count(obj) <= 1) : (efl_ref_count(obj) <= 0),
          EINA_FALSE
        };

        efl_event_callback_array_add(obj, _object_forced_death(), &force);

        ERR("Killing Zombie Object [%s:%i:%i]. Refs: %i:%i",
            efl_debug_name_get(obj), force.invalidated, force.noref,
            efl_ref_count(obj), ___efl_ref2_count(obj));
        ___efl_ref2_reset(obj);
        // This code explicitely bypass all refcounting to destroy them
        if (!force.invalidated) efl_del(obj);
        while (!force.destroyed) efl_unref(obj);

        if (!force.invalidated)
          {
             ERR("Zombie Object [%s] %s@%p could not be invalidated. "
                 "It seems like the call to efl_invalidated() wasn't "
                 "propagated to all the parent classes.",
                 efl_debug_name_get(obj), efl_class_name_get(obj), obj);
          }
        if (!force.destroyed)
          {
             ERR("Zombie Object [%s] %s@%p could not be destroyed. "
                 "It seems like the call to efl_destructor() wasn't "
                 "propagated to all the parent classes.",
                 efl_debug_name_get(obj), efl_class_name_get(obj), obj);
          }

        // Forcefully remove the object from layers
        EINA_INLIST_FOREACH(e->layers, lay)
          EINA_INLIST_FOREACH(lay->objects, o)
            if (o && (o->object == obj))
              {
                 ERR("Zombie Object [%s] %s@%p could not be removed "
                     "from the canvas list of objects. Maybe this object "
                     "was deleted but the call to efl_invalidated() "
                     "was not propagated to all the parent classes? "
                     "Forcibly removing it. This may leak! Refs: %i:%i",
                     efl_debug_name_get(obj), efl_class_name_get(obj), obj,
                     efl_ref_count(obj), ___efl_ref2_count(obj));
                 lay->objects = (Evas_Object_Protected_Data *)
                   eina_inlist_remove(EINA_INLIST_GET(lay->objects), EINA_INLIST_GET(o));
                 goto next_zombie;
              }

     next_zombie:
        continue;
     }

   eina_array_flush(&stash);

   // Destroying layers and their associated objects completely
   EINA_INLIST_FOREACH(e->layers, lay)
     evas_layer_free_objects(lay);
   evas_layer_clean(eo_e);

   efl_invalidate(efl_super(eo_e, MY_CLASS));
}

EOLIAN static void
_evas_canvas_efl_object_destructor(Eo *eo_e, Evas_Public_Data *e)
{
   Eina_Rectangle *r;
   Evas_Coord_Touch_Point *touch_point;
   Evas_Post_Render_Job *job;
   Evas_Layer *lay;
   Efl_Canvas_Output *evo;
   int i;

   if (e->layers)
     {
        // This should never happen as during invalidate we explicitely
        // destroy all layers. If they survive, we have a zombie appocallypse.
        Evas_Object_Protected_Data *o;

        CRI("The layers of %p are not empty !", eo_e);

        EINA_INLIST_FOREACH(e->layers, lay)
          EINA_INLIST_FOREACH(lay->objects, o)
            {
               CRI("Zombie object [%s] %s@%p still present.",
                   efl_debug_name_get(o->object), efl_class_name_get(o->object), o->object);
            }
     }

   evas_font_path_clear(eo_e);

   if (e->name_hash) eina_hash_free(e->name_hash);
   e->name_hash = NULL;

   EINA_LIST_FREE(e->damages, r)
      eina_rectangle_free(r);
   EINA_LIST_FREE(e->obscures, r)
      eina_rectangle_free(r);

   evas_fonts_zero_free();

   evas_event_callback_all_del(eo_e);
   evas_event_callback_cleanup(eo_e);

   EINA_LIST_FREE(e->touch_points, touch_point)
     free(touch_point);

   _evas_device_cleanup(eo_e);
   e->focused_by = eina_list_free(e->focused_by);

   while (e->seats)
     {
        Evas_Pointer_Seat *pseat = EINA_INLIST_CONTAINER_GET(e->seats, Evas_Pointer_Seat);

        eina_list_free(pseat->object.in);
        while (pseat->pointers)
          {
             Evas_Pointer_Data *pdata = EINA_INLIST_CONTAINER_GET(pseat->pointers, Evas_Pointer_Data);
             pseat->pointers = eina_inlist_remove(pseat->pointers, pseat->pointers);
             free(pdata);
          }
        e->seats = eina_inlist_remove(e->seats, e->seats);
        free(pseat);
     }

   /* Ector surface may require an existing output to finish its job */
   if (e->engine.func)
     e->engine.func->ector_destroy(_evas_engine_context(e), e->ector);
   /* cleanup engine backend */
   EINA_LIST_FREE(e->outputs, evo) efl_canvas_output_del(evo);
   if (e->engine.func)
     e->engine.func->engine_free(e->backend);

   for (i = 0; i < e->modifiers.mod.count; i++)
     free(e->modifiers.mod.list[i]);
   if (e->modifiers.mod.list) free(e->modifiers.mod.list);

   for (i = 0; i < e->locks.lock.count; i++)
     free(e->locks.lock.list[i]);
   if (e->locks.lock.list) free(e->locks.lock.list);

   if (e->engine.module) evas_module_unref(e->engine.module);

   eina_array_flush(&e->delete_objects);
   eina_inarray_flush(&e->active_objects);
   eina_array_flush(&e->restack_objects);
   eina_array_flush(&e->render_objects);
   eina_array_flush(&e->pending_objects);
   eina_array_flush(&e->obscuring_objects);
   eina_array_flush(&e->temporary_objects);
   eina_array_flush(&e->snapshot_objects);
   eina_array_flush(&e->clip_changes);
   eina_array_flush(&e->scie_unref_queue);
   eina_array_flush(&e->image_unref_queue);
   eina_array_flush(&e->glyph_unref_queue);
   eina_array_flush(&e->texts_unref_queue);
   eina_array_flush(&e->map_clip_objects);
   eina_hash_free(e->focused_objects);
   eina_array_flush(&e->render_post_change_objects);

   SLKL(e->post_render.lock);
   EINA_INLIST_FREE(e->post_render.jobs, job)
     {
        e->post_render.jobs = (Evas_Post_Render_Job *)
              eina_inlist_remove(EINA_INLIST_GET(e->post_render.jobs), EINA_INLIST_GET(job));
        free(job);
     }
   SLKU(e->post_render.lock);

   eina_lock_free(&(e->lock_objects));
   eina_spinlock_free(&(e->render.lock));
   eina_spinlock_free(&(e->post_render.lock));
   eina_hash_free(e->locks.masks);
   eina_hash_free(e->modifiers.masks);

   e->magic = 0;
   efl_destructor(efl_super(eo_e, MY_CLASS));
}

// It is now expected that the first output in the list is the default one
// manipulated by this set of legacy API

EAPI Evas_Engine_Info *
evas_engine_info_get(const Evas *obj)
{
   if (!obj) return NULL;

   Evas_Public_Data *e = efl_data_scope_get(obj, EVAS_CANVAS_CLASS);
   Efl_Canvas_Output *output;

   output = eina_list_data_get(e->outputs);
   if (!output)
     {
        output = efl_canvas_output_add((Evas*) obj);
     }
   if (!output) return NULL;
   e->output.legacy = EINA_TRUE;

   return efl_canvas_output_engine_info_get(output);
}

EAPI Eina_Bool
evas_engine_info_set(Evas *obj, Evas_Engine_Info *info)
{
   if (!obj) return EINA_FALSE;

   Evas_Public_Data *e = efl_data_scope_get(obj, EVAS_CANVAS_CLASS);
   Efl_Canvas_Output *output;

   output = eina_list_data_get(e->outputs);
   if (!output) return EINA_FALSE;
   if (!info) return EINA_FALSE;
   efl_canvas_output_view_set(output, 0, 0,
                              e->output.w, e->output.h);
   return efl_canvas_output_engine_info_set(output, info);
}

EOLIAN static Evas_Coord
_evas_canvas_coord_screen_x_to_world(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, int x)
{
   if (e->output.w == e->viewport.w) return e->viewport.x + x;
   else return (long long)e->viewport.x + (((long long)x * (long long)e->viewport.w) / (long long)e->output.w);
}

EOLIAN static Evas_Coord
_evas_canvas_coord_screen_y_to_world(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, int y)
{
   if (e->output.h == e->viewport.h) return e->viewport.y + y;
   else return (long long)e->viewport.y + (((long long)y * (long long)e->viewport.h) / (long long)e->output.h);
}

EOLIAN static int
_evas_canvas_coord_world_x_to_screen(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Evas_Coord x)
{
   if (e->output.w == e->viewport.w) return x - e->viewport.x;
   else return (int)((((long long)x - (long long)e->viewport.x) * (long long)e->output.w) /  (long long)e->viewport.w);
}

EOLIAN static int
_evas_canvas_coord_world_y_to_screen(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Evas_Coord y)
{
   if (e->output.h == e->viewport.h) return y - e->viewport.y;
   else return (int)((((long long)y - (long long)e->viewport.y) * (long long)e->output.h) /  (long long)e->viewport.h);
}

EOLIAN static Evas_Device *
_evas_canvas_default_device_get(const Eo *eo_e EINA_UNUSED,
                                Evas_Public_Data *e,
                                Evas_Device_Class klass)
{
   if (klass == (Evas_Device_Class)EFL_INPUT_DEVICE_TYPE_SEAT)
     return e->default_seat;
   if (klass == (Evas_Device_Class)EFL_INPUT_DEVICE_TYPE_MOUSE)
     return e->default_mouse;
   if (klass == (Evas_Device_Class)EFL_INPUT_DEVICE_TYPE_KEYBOARD)
     return e->default_keyboard;
   return NULL;
}

EAPI int
evas_render_method_lookup(const char *name)
{
   Evas_Module *em;

   if (!name) return RENDER_METHOD_INVALID;
   /* search on the engines list for the name */
   em = evas_module_find_type(EVAS_MODULE_TYPE_ENGINE, name);
   if (!em) return RENDER_METHOD_INVALID;

   return em->id_engine;
}

EAPI Eina_List *
evas_render_method_list(void)
{
   return evas_module_engine_list();
}

EAPI void
evas_render_method_list_free(Eina_List *list)
{
   const char *s;

   EINA_LIST_FREE(list, s) eina_stringshare_del(s);
}

EAPI Eina_Bool
evas_object_image_extension_can_load_get(const char *file)
{
   const char *tmp;
   Eina_Bool result;

   tmp = eina_stringshare_add(file);
   result = evas_common_extension_can_load_get(tmp);
   eina_stringshare_del(tmp);

   return result;
}

EAPI Eina_Bool
evas_object_image_extension_can_load_fast_get(const char *file)
{
   return evas_common_extension_can_load_get(file);
}

EOLIAN static void
_evas_canvas_pointer_output_xy_by_device_get(const Eo *eo_e EINA_UNUSED,
                                             Evas_Public_Data *e,
                                             Evas_Device *dev,
                                             int *x, int *y)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, dev);

   if (!pdata)
     {
        if (x) *x = 0;
        if (y) *y = 0;
     }
   else
     {
        if (x) *x = pdata->seat->x;
        if (y) *y = pdata->seat->y;
     }

}

EOLIAN static void
_evas_canvas_pointer_canvas_xy_by_device_get(const Eo *eo_e EINA_UNUSED,
                                             Evas_Public_Data *e,
                                             Evas_Device *dev,
                                             int *x, int *y)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, dev);

   if (!pdata)
     {
        if (x) *x = 0;
        if (y) *y = 0;
     }
   else
     {
        if (x) *x = pdata->seat->x;
        if (y) *y = pdata->seat->y;
     }
}

EOLIAN static unsigned int
_evas_canvas_pointer_button_down_mask_by_device_get(const Eo *eo_e EINA_UNUSED,
                                                    Evas_Public_Data *e,
                                                    Evas_Device *dev)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, dev);
   if (!pdata) return 0;
   return pdata->button;
}

EOLIAN static Eina_Bool
_evas_canvas_efl_canvas_pointer_pointer_inside_get(const Eo *eo_e EINA_UNUSED,
                                                   Evas_Public_Data *e,
                                                   Evas_Device *dev)
{
   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, dev);
   if (!pdata) return EINA_FALSE;
   return pdata->seat->inside;
}

EOLIAN static void
_evas_canvas_pointer_output_xy_get(const Eo *eo_e, Evas_Public_Data *e, int *x, int *y)
{
   return _evas_canvas_pointer_output_xy_by_device_get(eo_e, e, NULL, x, y);
}

EOLIAN static void
_evas_canvas_pointer_canvas_xy_get(const Eo *eo_e, Evas_Public_Data *e, Evas_Coord *x, Evas_Coord *y)
{
   return _evas_canvas_pointer_canvas_xy_by_device_get(eo_e, e, NULL, x, y);
}

EOLIAN static unsigned int
_evas_canvas_pointer_button_down_mask_get(const Eo *eo_e, Evas_Public_Data *e)
{
   return _evas_canvas_pointer_button_down_mask_by_device_get(eo_e, e, NULL);
}

EOLIAN static void
_evas_canvas_data_attach_set(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, void *data)
{
   e->attach_data = data;
}

EOLIAN static void*
_evas_canvas_data_attach_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return e->attach_data;
}

static void
_evas_canvas_focus_inout_dispatch(Eo *eo_e, Evas_Device *seat EINA_UNUSED,
                                  Eina_Bool in)
{
   efl_event_callback_legacy_call(eo_e,
                                  in ? EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN : EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_OUT,
                                  NULL);
}

EOLIAN static void
_evas_canvas_seat_focus_in(Eo *eo_e, Evas_Public_Data *e,
                           Evas_Device *seat)
{
   if (!seat) seat = e->default_seat;
   if (!seat || efl_input_device_type_get(seat) != EFL_INPUT_DEVICE_TYPE_SEAT) return;
   _evas_canvas_focus_inout_dispatch(eo_e, seat, EINA_TRUE);
}

EOLIAN static void
_evas_canvas_seat_focus_out(Eo *eo_e, Evas_Public_Data *e,
                            Evas_Device *seat)
{
   if (!seat) seat = e->default_seat;
   if (!seat || efl_input_device_type_get(seat) != EFL_INPUT_DEVICE_TYPE_SEAT) return;
   _evas_canvas_focus_inout_dispatch(eo_e, seat, EINA_FALSE);
}

EOLIAN static void
_evas_canvas_focus_in(Eo *eo_e, Evas_Public_Data *e)
{
   _evas_canvas_seat_focus_in(eo_e, e, NULL);
}

EOLIAN static void
_evas_canvas_focus_out(Eo *eo_e, Evas_Public_Data *e)
{
   _evas_canvas_seat_focus_out(eo_e, e, NULL);
}

EOLIAN static Eina_Bool
_evas_canvas_seat_focus_state_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e,
                                  Evas_Device *seat)
{
   if (!seat) seat = e->default_seat;
   return eina_list_data_find(e->focused_by, seat) ? EINA_TRUE : EINA_FALSE;
}

EOLIAN static Eina_Bool
_evas_canvas_focus_state_get(const Eo *eo_e, Evas_Public_Data *e)
{
   return _evas_canvas_seat_focus_state_get(eo_e, e, NULL);
}

EOLIAN static Eina_Bool
_evas_canvas_changed_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return e->changed;
}

EOLIAN static void
_evas_canvas_nochange_push(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   e->nochange++;
}

EOLIAN static void
_evas_canvas_nochange_pop(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   e->nochange--;
}

void
_evas_walk(Evas_Public_Data *e)
{
   efl_ref(e->evas);
}

void
_evas_unwalk(Evas_Public_Data *e)
{
   efl_unref(e->evas);
}

EAPI const char *
evas_load_error_str(Evas_Load_Error error)
{
   switch (error)
     {
      case EVAS_LOAD_ERROR_NONE:
	 return "No error on load";
      case EVAS_LOAD_ERROR_GENERIC:
	 return "A non-specific error occurred";
      case EVAS_LOAD_ERROR_DOES_NOT_EXIST:
	 return "File (or file path) does not exist";
      case EVAS_LOAD_ERROR_PERMISSION_DENIED:
	 return "Permission deinied to an existing file (or path)";
      case EVAS_LOAD_ERROR_RESOURCE_ALLOCATION_FAILED:
	 return "Allocation of resources failure prevented load";
      case EVAS_LOAD_ERROR_CORRUPT_FILE:
	 return "File corrupt (but was detected as a known format)";
      case EVAS_LOAD_ERROR_UNKNOWN_FORMAT:
	 return "File is not a known format";
      default:
	 return "Unknown error";
     }
}

EAPI void
evas_color_hsv_to_rgb(float h, float s, float v, int *r, int *g, int *b)
{
   evas_common_convert_color_hsv_to_rgb(h, s, v, r, g, b);
}

EAPI void
evas_color_rgb_to_hsv(int r, int g, int b, float *h, float *s, float *v)
{
   evas_common_convert_color_rgb_to_hsv(r, g, b, h, s, v);
}

EAPI void
evas_color_argb_premul(int a, int *r, int *g, int *b)
{
   evas_common_convert_color_argb_premul(a, r, g, b);
}

EAPI void
evas_color_argb_unpremul(int a, int *r, int *g, int *b)
{
   evas_common_convert_color_argb_unpremul(a, r, g, b);
}

EAPI void
evas_data_argb_premul(unsigned int *data, unsigned int len)
{
   if (!data || (len < 1)) return;
   evas_common_convert_argb_premul(data, len);
}

EAPI void
evas_data_argb_unpremul(unsigned int *data, unsigned int len)
{
   if (!data || (len < 1)) return;
   evas_common_convert_argb_unpremul(data, len);
}

EOLIAN static Eo *
_evas_canvas_efl_object_provider_find(const Eo *eo_e,
                                      Evas_Public_Data *e EINA_UNUSED,
                                      const Efl_Class *klass)
{
   if (klass == EVAS_CANVAS_CLASS)
     return (Eo *)eo_e;
   else if (klass == EFL_LOOP_CLASS)
     return efl_main_loop_get();
   else if (klass == EFL_CANVAS_GESTURE_MANAGER_CLASS)
     return e->gesture_manager;
   return efl_provider_find(efl_super(eo_e, MY_CLASS), klass);
}

EOLIAN static Efl_Loop *
_evas_canvas_efl_loop_consumer_loop_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e EINA_UNUSED)
{
   return efl_main_loop_get();
}

Ector_Surface *
evas_ector_get(Evas_Public_Data *e)
{
   if (!e->ector)
     e->ector = e->engine.func->ector_create(_evas_engine_context(e));
   return e->ector;
}

EAPI Evas_BiDi_Direction
evas_language_direction_get(void)
{
   return evas_common_language_direction_get();
}

EAPI void
evas_language_reinit(void)
{
   evas_common_language_reinit();
}

static void
_image_data_unset(Evas_Object_Protected_Data *obj, Eina_List **list)
{
   if (obj->is_smart)
     {
        Evas_Object_Protected_Data *obj2;

        EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(obj->object), obj2)
          _image_data_unset(obj2, list);
        return;
     }
#define CHECK(TYPE, STRUCT, FREE) \
   if (efl_isa(obj->object, TYPE))\
     {\
        STRUCT *data = efl_data_scope_get(obj->object, TYPE);\
        FREE; \
        data->engine_data = NULL;\
     }
   CHECK(EFL_CANVAS_IMAGE_INTERNAL_CLASS, Evas_Image_Data,
         ENFN->image_free(ENC, data->engine_data))
   else CHECK(EFL_CANVAS_POLYGON_CLASS, Efl_Canvas_Polygon_Data,
        data->engine_data =
          obj->layer->evas->engine.func->polygon_points_clear(ENC,
                                                              data->engine_data))
   else return;
#undef CHECK
   evas_object_ref(obj->object);
   *list = eina_list_append(*list, obj->object);
}

EAPI Eina_List *
_evas_canvas_image_data_unset(Evas *eo_e)
{
   Evas_Public_Data *e = efl_data_scope_get(eo_e, MY_CLASS);
   Evas_Layer *lay;
   Eina_List *list = NULL;

   EINA_INLIST_FOREACH(e->layers, lay)
     {
        Evas_Object_Protected_Data *o;
        EINA_INLIST_FOREACH(lay->objects, o)
          {
             if (!o->delete_me)
               _image_data_unset(o, &list);
          }
     }
   return list;
}

static void
_image_image_data_regenerate(Evas_Object *eo_obj, Evas_Object_Protected_Data *obj, Evas_Image_Data *data)
{
   unsigned int orient = data->cur->orient;

   _evas_image_load(eo_obj, obj, data);
   EINA_COW_IMAGE_STATE_WRITE_BEGIN(data, state_write)
     {
        state_write->has_alpha = !state_write->has_alpha;
        state_write->orient = -1;
     }
   EINA_COW_IMAGE_STATE_WRITE_END(data, state_write);
   evas_object_image_alpha_set(eo_obj, !data->cur->has_alpha);
   _evas_image_orientation_set(eo_obj, data, orient);
}

static void
_image_data_regenerate(Evas_Object *eo_obj)
{
   Evas_Object_Protected_Data *obj;

   obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   evas_object_change(eo_obj, obj);
#define CHECK(TYPE, STRUCT, REGEN) \
   if (efl_isa(eo_obj, TYPE))\
     {\
        STRUCT *data = efl_data_scope_get(eo_obj, TYPE);\
        REGEN; \
     }
   CHECK(EFL_CANVAS_IMAGE_INTERNAL_CLASS, Evas_Image_Data, _image_image_data_regenerate(eo_obj, obj, data))
   else CHECK(EFL_CANVAS_IMAGE_CLASS, Evas_Image_Data, _image_image_data_regenerate(eo_obj, obj, data))
   //else CHECK(EFL_CANVAS_VG_OBJECT_CLASS, Efl_Canvas_Vg_Object_Data,)
   //else CHECK(EFL_CANVAS_POLYGON_CLASS, Efl_Canvas_Polygon_Data,)
}

EAPI void
_evas_canvas_image_data_regenerate(Eina_List *list)
{
   Evas_Object *eo_obj;

   EINA_LIST_FREE(list, eo_obj)
     {
        _image_data_regenerate(eo_obj);
        evas_object_unref(eo_obj);
     }
}


EOLIAN void
_evas_canvas_image_cache_flush(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   evas_canvas_async_block(e);
   evas_render_rendering_wait(e);
   if (_evas_engine_context(e))
     e->engine.func->image_cache_flush(_evas_engine_context(e));
}

EOLIAN void
_evas_canvas_image_cache_reload(Eo *eo_e, Evas_Public_Data *e)
{
   Evas_Layer *layer;

   evas_canvas_async_block(e);
   evas_image_cache_flush(eo_e);
   EINA_INLIST_FOREACH(e->layers, layer)
     {
        Evas_Object_Protected_Data *obj;

        layer->walking_objects++;
        EINA_INLIST_FOREACH(layer->objects, obj)
          {
             if (efl_isa(obj->object, MY_CLASS))
               {
                  _evas_image_unload(obj->object, obj, 1);
                  evas_object_inform_call_image_unloaded(obj->object);
               }
          }
        layer->walking_objects--;
        _evas_layer_flush_removes(layer);
     }
   evas_image_cache_flush(eo_e);
   EINA_INLIST_FOREACH(e->layers, layer)
     {
        Evas_Object_Protected_Data *obj;

        layer->walking_objects++;
        EINA_INLIST_FOREACH(layer->objects, obj)
          {
             if (efl_isa(obj->object, MY_CLASS))
               {
                  Evas_Image_Data *o = efl_data_scope_get(obj->object, MY_CLASS);
                  _evas_image_load(obj->object, obj, o);
                  o->changed = EINA_TRUE;
                  evas_object_change(obj->object, obj);
               }
          }
        layer->walking_objects--;
        _evas_layer_flush_removes(layer);
     }
   evas_image_cache_flush(eo_e);
}

EOLIAN static void
_evas_canvas_image_cache_set(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, int size)
{
   if (size < 0) size = 0;
   evas_canvas_async_block(e);
   evas_render_rendering_wait(e);
   if (_evas_engine_context(e))
     e->engine.func->image_cache_set(_evas_engine_context(e), size);
}

EOLIAN static int
_evas_canvas_image_cache_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   if (_evas_engine_context(e))
     return e->engine.func->image_cache_get(_evas_engine_context(e));
   return -1;
}

EOLIAN static Eina_Bool
_evas_canvas_efl_canvas_scene_image_max_size_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Eina_Size2D *max)
{
   int w = 0, h = 0;

   if (max) *max = EINA_SIZE2D(0xffff, 0xffff);
   if (!e->engine.func->image_max_size_get) return EINA_FALSE;
   if (!max) return EINA_TRUE;
   e->engine.func->image_max_size_get(_evas_engine_context(e), &w, &h);
   *max = EINA_SIZE2D(w, h);
   return EINA_TRUE;
}

/* Legacy deprecated functions */

EAPI void
evas_output_framespace_set(Evas *eo_e, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if ((x == e->framespace.x) && (y == e->framespace.y) &&
       (w == e->framespace.w) && (h == e->framespace.h)) return;
   evas_canvas_async_block(e);
   e->framespace.x = x;
   e->framespace.y = y;
   e->framespace.w = w;
   e->framespace.h = h;
   e->framespace.changed = 1;
   e->output_validity++;
   e->changed = 1;
}

EAPI void
evas_output_framespace_get(const Evas *eo_e, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if (x) *x = e->framespace.x;
   if (y) *y = e->framespace.y;
   if (w) *w = e->framespace.w;
   if (h) *h = e->framespace.h;
}

EAPI void
evas_output_method_set(Evas *eo_e, int render_method)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   Evas_Module *em;

   /* if our engine to set it to is invalid - abort */
   if (render_method == RENDER_METHOD_INVALID) return;
   /* if the engine is already set up - abort */
   if (e->output.render_method != RENDER_METHOD_INVALID) return;
   /* Request the right engine. */
   em = evas_module_engine_get(render_method);
   if (!em) return;
   if (em->id_engine != render_method) return;
   if (!evas_module_load(em)) return;

   evas_canvas_async_block(e);
   /* set the correct render */
   e->output.render_method = render_method;
   e->engine.func = (em->functions);
   evas_module_use(em);
   if (e->engine.module) evas_module_unref(e->engine.module);
   e->engine.module = em;
   evas_module_ref(em);

   /* Initialize the engine first */
   e->backend = e->engine.func->engine_new();

   /* get the engine info struct */
   if (e->engine.func->info_size)
     {
        Efl_Canvas_Output *output;
        Eina_List *l;

        EINA_LIST_FOREACH(e->outputs, l, output)
          efl_canvas_output_info_get(e, output);
     }
   else
     {
        CRI("Engine not up to date no info size provided.");
     }

   // Wayland/drm already handles seats.
   if (em->definition && (eina_streq(em->definition->name, "wayland_shm") ||
                          eina_streq(em->definition->name, "wayland_egl") ||
                          eina_streq(em->definition->name, "drm") ||
                          eina_streq(em->definition->name, "gl_drm")))
     {
        Evas_Pointer_Seat *pseat = calloc(1, sizeof(Evas_Pointer_Seat));
        e->seats = eina_inlist_append(e->seats, EINA_INLIST_GET(pseat));
        return;
     }

   e->default_seat = evas_device_add_full(eo_e, "default", "The default seat",
                                          NULL, NULL, EVAS_DEVICE_CLASS_SEAT,
                                          EVAS_DEVICE_SUBCLASS_NONE);
   evas_device_seat_id_set(e->default_seat, 1);
   e->default_mouse = evas_device_add_full(eo_e, "Mouse",
                                           "The default mouse",
                                           e->default_seat, NULL,
                                           EVAS_DEVICE_CLASS_MOUSE,
                                           EVAS_DEVICE_SUBCLASS_NONE);
   e->default_keyboard = evas_device_add_full(eo_e, "Keyboard",
                                              "The default keyboard",
                                              e->default_seat, NULL,
                                              EVAS_DEVICE_CLASS_KEYBOARD,
                                              EVAS_DEVICE_SUBCLASS_NONE);
}

EAPI int
evas_output_method_get(const Evas *eo_e)
{
   EVAS_TYPE_CHECK(eo_e, RENDER_METHOD_INVALID);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   return e->output.render_method;
}

EAPI void
evas_output_size_set(Evas *eo_e, int w, int h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if ((w == e->output.w) && (h == e->output.h)) return;
   if (w < 1) w = 1;
   if (h < 1) h = 1;

   evas_canvas_async_block(e);
   e->output.w = w;
   e->output.h = h;
   e->output_validity++;
   e->changed = 1;

   if (e->output.legacy)
     {
        Efl_Canvas_Output *output;

        output = eina_list_data_get(e->outputs);
        efl_canvas_output_view_set(output, 0, 0, w, h);
     }

   evas_render_invalidate(eo_e);
}

EAPI void
evas_output_size_get(const Evas *eo_e, int *w, int *h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if (w) *w = e->output.w;
   if (h) *h = e->output.h;
}

EAPI void
evas_output_viewport_set(Evas *eo_e, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if ((x == e->viewport.x) && (y == e->viewport.y) &&
       (w == e->viewport.w) && (h == e->viewport.h)) return;
   if (w <= 0) return;
   if (h <= 0) return;
   if ((x != 0) || (y != 0))
     {
	ERR("Compat error. viewport x,y != 0,0 not supported");
	x = 0;
	y = 0;
     }
   evas_canvas_async_block(e);
   e->viewport.x = x;
   e->viewport.y = y;
   e->viewport.w = w;
   e->viewport.h = h;
   e->viewport.changed = 1;
   e->output_validity++;
   e->changed = 1;
   evas_event_callback_call(e->evas, EVAS_CALLBACK_CANVAS_VIEWPORT_RESIZE, NULL);
}

EAPI void
evas_output_viewport_get(const Evas *eo_e, Evas_Coord *x, Evas_Coord *y, Evas_Coord *w, Evas_Coord *h)
{
   EVAS_TYPE_CHECK(eo_e);

   Evas_Public_Data *e = efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS);

   if (x) *x = e->viewport.x;
   if (y) *y = e->viewport.y;
   if (w) *w = e->viewport.w;
   if (h) *h = e->viewport.h;
}

Evas_Pointer_Data *
_evas_pointer_data_by_device_get(Evas_Public_Data *edata, Evas_Device *pointer)
{
   Evas_Pointer_Data *pdata;
   Evas_Pointer_Seat *pseat;
   Eo *seat;

   if (!pointer)
     pointer = edata->default_mouse;
   if (!pointer) return NULL;
   seat = efl_input_device_seat_get(pointer);
   if (!seat) return NULL;

   EINA_INLIST_FOREACH(edata->seats, pseat)
     EINA_INLIST_FOREACH(pseat->pointers, pdata)
       {
          if (pointer == seat)
            {
               if (pseat->seat == seat) return pdata;
            }
          else if (pdata->pointer == pointer) return pdata;
       }
   return NULL;
}

Evas_Pointer_Data *
_evas_pointer_data_add(Evas_Public_Data *edata, Evas_Device *pointer)
{
   Evas_Pointer_Data *pdata;
   Evas_Pointer_Seat *pseat = NULL;
   Eo *seat;

   seat = efl_input_device_seat_get(pointer);
   EINA_SAFETY_ON_NULL_RETURN_VAL(seat, NULL);
   EINA_INLIST_FOREACH(edata->seats, pseat)
     if (pseat->seat == seat) break;
   if (!pseat)
     {
        pseat = calloc(1, sizeof(Evas_Pointer_Seat));
        EINA_SAFETY_ON_NULL_RETURN_VAL(pseat, NULL);
        pseat->seat = seat;
        edata->seats = eina_inlist_append(edata->seats, EINA_INLIST_GET(pseat));
     }
   pdata = calloc(1, sizeof(Evas_Pointer_Data));
   if (!pdata)
     {
        if (!pseat->pointers)
          {
             edata->seats = eina_inlist_remove(edata->seats, EINA_INLIST_GET(pseat));
             free(pseat);
          }
        ERR("alloc fail");
        return NULL;
     }

   pdata->pointer = pointer;
   pdata->seat = pseat;
   pseat->pointers = eina_inlist_append(pseat->pointers, EINA_INLIST_GET(pdata));
   return pdata;
}

void
_evas_pointer_data_remove(Evas_Public_Data *edata, Evas_Device *pointer, Eina_Bool nofree)
{
   Evas_Pointer_Data *pdata;
   Evas_Pointer_Seat *pseat;
   Evas_Pointer_Seat *hit = NULL;

   EINA_INLIST_FOREACH(edata->seats, pseat)
     {
        EINA_INLIST_FOREACH(pseat->pointers, pdata)
          if (pdata->pointer == pointer)
            {
               if (!nofree)
                 {
                    pseat->pointers = eina_inlist_remove(pseat->pointers, EINA_INLIST_GET(pdata));
                    free(pdata);
                 }
               hit = pseat;
               break;
            }
     }
   EINA_SAFETY_ON_NULL_RETURN(hit);
   if (hit->pointers) return;
   hit->object.in = eina_list_free(hit->object.in);
   if (!nofree)
     {
        edata->seats = eina_inlist_remove(edata->seats, EINA_INLIST_GET(hit));
        free(hit);
     }
}

Eina_List *
_evas_pointer_list_in_rect_get(Evas_Public_Data *edata, Evas_Object *obj,
                               Evas_Object_Protected_Data *obj_data,
                               int w, int h)
{
   Eina_List *list = NULL;
   Evas_Pointer_Seat *pseat;

   EINA_INLIST_FOREACH(edata->seats, pseat)
     {
        Evas_Pointer_Data *pdata;
        if (!evas_object_is_in_output_rect(obj, obj_data, pseat->x, pseat->y, w, h)) continue;
        pdata = EINA_INLIST_CONTAINER_GET(pseat->pointers, Evas_Pointer_Data);
        if (pdata)
          list = eina_list_append(list, pdata);
     }

   return list;
}

static Eina_Inlist *
get_layer_objects(Evas_Layer *l)
{
   if ((!l) || (!l->objects)) return NULL;
   return (EINA_INLIST_GET(l->objects));
}

typedef struct _Efl_Canvas_Iterator
{
   Eina_Iterator  iterator;
   Eina_List     *list;
   Eina_Iterator *real_iterator;
   Eo            *object;
} Efl_Canvas_Iterator;

/* this iterator is the same as efl_ui_box */
static Eina_Bool
_efl_canvas_iterator_next(Efl_Canvas_Iterator *it, void **data)
{
   Efl_Gfx_Entity *sub;

   if (!it->object) return EINA_FALSE;
   if (!eina_iterator_next(it->real_iterator, (void **) &sub))
     return EINA_FALSE;

   if (data) *data = sub;
   return EINA_TRUE;
}

static Eo *
_efl_canvas_iterator_get_container(Efl_Canvas_Iterator *it)
{
   return it->object;
}

static void
_efl_canvas_iterator_free(Efl_Canvas_Iterator *it)
{
   eina_iterator_free(it->real_iterator);
   efl_wref_del(it->object, &it->object);
   eina_list_free(it->list);
   free(it);
}

EAPI Eina_Iterator *
efl_canvas_iterator_create(Eo *obj, Eina_Iterator *real_iterator, Eina_List *list)
{
   Efl_Canvas_Iterator *it;

   it = calloc(1, sizeof(*it));
   if (!it) return NULL;

   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);

   it->list = list;
   it->real_iterator = real_iterator;
   it->iterator.version = EINA_ITERATOR_VERSION;
   it->iterator.next = FUNC_ITERATOR_NEXT(_efl_canvas_iterator_next);
   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_efl_canvas_iterator_get_container);
   it->iterator.free = FUNC_ITERATOR_FREE(_efl_canvas_iterator_free);
   efl_wref_add(obj, &it->object);

   return &it->iterator;
}

EOLIAN static Evas_Object*
_evas_canvas_efl_canvas_scene_object_top_at_xy_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Evas_Layer *lay;
   int xx, yy;

   xx = pos.x;
   yy = pos.y;
////   xx = evas_coord_world_x_to_screen(eo_e, x);
////   yy = evas_coord_world_y_to_screen(eo_e, y);
   EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(e->layers)), lay)
     {
        Evas_Object *eo_obj;
        Evas_Object_Protected_Data *obj;

        EINA_INLIST_REVERSE_FOREACH(get_layer_objects(lay), obj)
          {
             eo_obj = obj->object;
             if (obj->delete_me) continue;
             if ((!include_pass_events_objects) &&
                 (evas_event_passes_through(eo_obj, obj))) continue;
             if (evas_object_is_source_invisible(eo_obj, obj)) continue;
             if ((!include_hidden_objects) && (!obj->cur->visible)) continue;
             evas_object_clip_recalc(obj);
             if ((evas_object_is_in_output_rect(eo_obj, obj, xx, yy, 1, 1)) &&
                 (!obj->clip.clipees) &&
                 RECTS_INTERSECT(xx, yy, 1, 1,
                   obj->cur->geometry.x, obj->cur->geometry.y,
                   obj->cur->geometry.w, obj->cur->geometry.h))
               return eo_obj;
          }
     }
   return NULL;
}

EAPI Evas_Object*
evas_object_top_at_xy_get(Eo *eo_e, Evas_Coord x, Evas_Coord y, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_Position2D pos = {x, y};
   return efl_canvas_scene_object_top_at_xy_get(eo_e, pos, include_pass_events_objects, include_hidden_objects);
}

EAPI Evas_Object *
evas_object_top_at_pointer_get(const Evas *eo_e)
{
   Evas_Public_Data *e = efl_isa(eo_e, EVAS_CANVAS_CLASS) ?
            efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS) : NULL;
   if (!e) return NULL;

   Evas_Pointer_Data *pdata = _evas_pointer_data_by_device_get(e, NULL);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pdata, NULL);
   return efl_canvas_scene_object_top_at_xy_get((Eo *)eo_e, EINA_POSITION2D(pdata->seat->x, pdata->seat->y), EINA_TRUE, EINA_TRUE);
}

EOLIAN Evas_Object*
_evas_canvas_efl_canvas_scene_object_top_in_rectangle_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Eina_Rect rect, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Evas_Layer *lay;
   int xx, yy, ww, hh;

   xx = rect.x;
   yy = rect.y;
   ww = rect.w;
   hh = rect.h;
////   xx = evas_coord_world_x_to_screen(eo_e, x);
////   yy = evas_coord_world_y_to_screen(eo_e, y);
////   ww = evas_coord_world_x_to_screen(eo_e, w);
////   hh = evas_coord_world_y_to_screen(eo_e, h);
   if (ww < 1) ww = 1;
   if (hh < 1) hh = 1;
   EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(e->layers)), lay)
     {
        Evas_Object *eo_obj;
        Evas_Object_Protected_Data *obj;

        EINA_INLIST_REVERSE_FOREACH(get_layer_objects(lay), obj)
          {
             eo_obj = obj->object;
             if (obj->delete_me) continue;
             if ((!include_pass_events_objects) &&
                 (evas_event_passes_through(eo_obj, obj))) continue;
             if (evas_object_is_source_invisible(eo_obj, obj)) continue;
             if ((!include_hidden_objects) && (!obj->cur->visible)) continue;
             evas_object_clip_recalc(obj);
             if ((evas_object_is_in_output_rect(eo_obj, obj, xx, yy, ww, hh)) &&
                 (!obj->clip.clipees) &&
                 RECTS_INTERSECT(xx, yy, ww, hh,
                   obj->cur->geometry.x, obj->cur->geometry.y,
                   obj->cur->geometry.w, obj->cur->geometry.h)) return eo_obj;
          }
     }
   return NULL;
}

EAPI Evas_Object *
evas_object_top_in_rectangle_get(const Eo *obj, int x, int y, int w, int h, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   return efl_canvas_scene_object_top_in_rectangle_get(obj, EINA_RECT(x, y, w, h), include_pass_events_objects, include_hidden_objects);
}

static Eina_List *
_efl_canvas_evas_canvas_objects_at_xy_get_helper(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, int x, int y, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *in = NULL;
   Evas_Layer *lay;
   int xx, yy;

   xx = x;
   yy = y;
////   xx = evas_coord_world_x_to_screen(eo_e, x);
////   yy = evas_coord_world_y_to_screen(eo_e, y);
   EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(e->layers)), lay)
     {
        Evas_Object *eo_obj;
        Evas_Object_Protected_Data *obj;

        EINA_INLIST_REVERSE_FOREACH(get_layer_objects(lay), obj)
          {
             eo_obj = obj->object;
             // FIXME - Daniel: we don't know yet how to handle the next line
             if (obj->delete_me) continue;
             if ((!include_pass_events_objects) &&
                   (evas_event_passes_through(eo_obj, obj))) continue;
             if (evas_object_is_source_invisible(eo_obj, obj)) continue;
             if ((!include_hidden_objects) && (!obj->cur->visible)) continue;
             evas_object_clip_recalc(obj);
             if ((evas_object_is_in_output_rect(eo_obj, obj, xx, yy, 1, 1)) &&
                 (!obj->clip.clipees))
               {
                  // evas_object_is_in_output_rect is based on the clip which
                  // may be larger than the geometry (bounding box)
                  if (!RECTS_INTERSECT(xx, yy, 1, 1,
                                       obj->cur->geometry.x,
                                       obj->cur->geometry.y,
                                       obj->cur->geometry.w,
                                       obj->cur->geometry.h))
                    continue;
                  in = eina_list_prepend(in, eo_obj);
               }
          }
     }
   return in;
}

EOLIAN static Eina_Iterator *
_evas_canvas_efl_canvas_scene_objects_at_xy_get(Eo *eo_e, Evas_Public_Data *e, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *l = _efl_canvas_evas_canvas_objects_at_xy_get_helper(eo_e, e, pos.x, pos.y, include_pass_events_objects, include_hidden_objects);
   if (l) return efl_canvas_iterator_create(eo_e, eina_list_iterator_new(l), l);
   return NULL;
}

/**
 * Retrieves the objects in the given rectangle region
 * @param   eo_e The given evas object.
 * @param   x The horizontal coordinate.
 * @param   y The vertical coordinate.
 * @param   w The width size.
 * @param   h The height size.
 * @param   include_pass_events_objects Boolean Flag to include or not pass events objects
 * @param   include_hidden_objects Boolean Flag to include or not hidden objects
 * @return  The list of evas object in the rectangle region.
 *
 */
static Eina_List*
_efl_canvas_objects_in_rectangle_get_helper(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *in = NULL;
   Evas_Layer *lay;
   int xx, yy, ww, hh;

   xx = x;
   yy = y;
   ww = w;
   hh = h;
////   xx = evas_coord_world_x_to_screen(eo_e, x);
////   yy = evas_coord_world_y_to_screen(eo_e, y);
////   ww = evas_coord_world_x_to_screen(eo_e, w);
////   hh = evas_coord_world_y_to_screen(eo_e, h);
   if (ww < 1) ww = 1;
   if (hh < 1) hh = 1;
   EINA_INLIST_REVERSE_FOREACH((EINA_INLIST_GET(e->layers)), lay)
     {
        Evas_Object *eo_obj;
        Evas_Object_Protected_Data *obj;

        EINA_INLIST_REVERSE_FOREACH(get_layer_objects(lay), obj)
          {
             eo_obj = obj->object;
             // FIXME - Daniel: we don't know yet how to handle the next line
             if (obj->delete_me) continue;
             if ((!include_pass_events_objects) &&
                 (evas_event_passes_through(eo_obj, obj))) continue;
             if (evas_object_is_source_invisible(eo_obj, obj)) continue;
             if ((!include_hidden_objects) && (!obj->cur->visible)) continue;
             evas_object_clip_recalc(obj);
             if ((evas_object_is_in_output_rect(eo_obj, obj, xx, yy, ww, hh)) &&
                 (!obj->clip.clipees))
               {
                  if (!RECTS_INTERSECT(xx, yy, ww, hh,
                                       obj->cur->geometry.x,
                                       obj->cur->geometry.y,
                                       obj->cur->geometry.w,
                                       obj->cur->geometry.h))
                    continue;
                  in = eina_list_prepend(in, eo_obj);
               }
          }
     }
   return in;
}


EOLIAN static Eina_Iterator*
_evas_canvas_efl_canvas_scene_objects_in_rectangle_get(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, Eina_Rect rect, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *l = _efl_canvas_objects_in_rectangle_get_helper(eo_e, e, rect.x, rect.y, rect.w, rect.h, include_pass_events_objects, include_hidden_objects);
   if (!l) return NULL;
   return efl_canvas_iterator_create(eo_e, eina_list_iterator_new(l), l);
}

EAPI Eina_List *
evas_objects_in_rectangle_get(const Evas_Canvas *eo_e, int x, int y, int w, int h, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   EVAS_LEGACY_API(eo_e, e, NULL);
   return _efl_canvas_objects_in_rectangle_get_helper(eo_e, e, x, y, w, h, include_pass_events_objects, include_hidden_objects);
}

/* font related api */

EOLIAN static void
_evas_canvas_font_path_clear(Eo *eo_e EINA_UNUSED, Evas_Public_Data *evas)
{
   evas_canvas_async_block(evas);
   while (evas->font_path)
     {
  eina_stringshare_del(evas->font_path->data);
  evas->font_path = eina_list_remove(evas->font_path, evas->font_path->data);
     }
}

EOLIAN static void
_evas_canvas_font_path_append(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, const char *path)
{
   if (!path) return;
   evas_canvas_async_block(e);
   e->font_path = eina_list_append(e->font_path, eina_stringshare_add(path));
}

EOLIAN static void
_evas_canvas_font_path_prepend(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, const char *path)
{
   if (!path) return;
   evas_canvas_async_block(e);
   e->font_path = eina_list_prepend(e->font_path, eina_stringshare_add(path));
}

EOLIAN static const Eina_List*
_evas_canvas_font_path_list(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return e->font_path;
}

EOLIAN static void
_evas_canvas_font_cache_flush(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   evas_canvas_async_block(e);
   evas_render_rendering_wait(e);
   if (_evas_engine_context(e))
     e->engine.func->font_cache_flush(_evas_engine_context(e));
}

EOLIAN static void
_evas_canvas_font_cache_set(Eo *eo_e EINA_UNUSED, Evas_Public_Data *e, int size)
{
   if (size < 0) size = 0;
   evas_canvas_async_block(e);
   evas_render_rendering_wait(e);
   if (_evas_engine_context(e))
     e->engine.func->font_cache_set(_evas_engine_context(e), size);
}

EOLIAN static int
_evas_canvas_font_cache_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   if (_evas_engine_context(e))
     return e->engine.func->font_cache_get(_evas_engine_context(e));
   return -1;
}

EOLIAN static Eina_List*
_evas_canvas_font_available_list(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *pd)
{
   return evas_font_dir_available_list(pd->font_path);
}

static void
evas_font_object_rehint(Evas_Object *eo_obj)
{
   Evas_Object_Protected_Data *obj = efl_data_scope_get(eo_obj, EFL_CANVAS_OBJECT_CLASS);
   if (obj->is_smart)
     {
  EINA_INLIST_FOREACH(evas_object_smart_members_get_direct(eo_obj), obj)
    evas_font_object_rehint(obj->object);
     }
   else
     {
  if (!strcmp(obj->type, "text"))
    _evas_object_text_rehint(eo_obj);
  if (!strcmp(obj->type, "textblock"))
    _evas_object_textblock_rehint(eo_obj);
     }
}

EAPI void
evas_font_hinting_set(Eo *eo_e, Evas_Font_Hinting_Flags hinting)
{
   Evas_Layer *lay;

   EVAS_LEGACY_API(eo_e, e);
   evas_canvas_async_block(e);
   if (e->hinting == hinting) return;
   e->hinting = hinting;

   EINA_INLIST_FOREACH(e->layers, lay)
     {
  Evas_Object_Protected_Data *obj;

  EINA_INLIST_FOREACH(lay->objects, obj)
    evas_font_object_rehint(obj->object);
     }
}

EAPI Evas_Font_Hinting_Flags
evas_font_hinting_get(const Evas *eo_e)
{
   EVAS_LEGACY_API(eo_e, e, EVAS_FONT_HINTING_NONE);
   return e->hinting;
}

EAPI Eina_Bool
evas_font_hinting_can_hint(const Evas *eo_e, Evas_Font_Hinting_Flags hinting)
{
   EVAS_LEGACY_API(eo_e, e, EINA_FALSE);
   if (e->engine.func->font_hinting_can_hint && _evas_engine_context(e))
     return e->engine.func->font_hinting_can_hint(_evas_engine_context(e),
                                                  hinting);
   else return EINA_FALSE;
}

EAPI void
evas_font_available_list_free(Evas *eo_e, Eina_List *available)
{
   EVAS_TYPE_CHECK(eo_e);

   evas_font_dir_available_list_free(available);
}


EOLIAN static void
_evas_canvas_efl_canvas_scene_group_objects_calculate(Eo *eo_e, Evas_Public_Data *o EINA_UNUSED)
{
   evas_call_smarts_calculate(eo_e);
}

EAPI void
evas_smart_objects_calculate(Eo *eo_e)
{
   EVAS_TYPE_CHECK(eo_e);
   evas_call_smarts_calculate(eo_e);
}

EOLIAN Eina_Bool
_evas_canvas_efl_canvas_scene_group_objects_calculating_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return !!e->in_smart_calc;
}

EAPI Eina_Bool
evas_smart_objects_calculating_get(const Eo *obj)
{
   EVAS_TYPE_CHECK(obj, EINA_FALSE);
   return efl_canvas_scene_group_objects_calculating_get(obj);
}

EOLIAN int
_evas_canvas_smart_objects_calculate_count_get(const Eo *eo_e EINA_UNUSED, Evas_Public_Data *e)
{
   return e->smart_calc_count;
}
/* Legacy EAPI */

EAPI Eina_Bool
evas_pointer_inside_get(const Evas *obj)
{
   EVAS_TYPE_CHECK(obj, EINA_FALSE);
   return efl_canvas_pointer_inside_get(obj, NULL);
}

EAPI Eina_Bool
evas_pointer_inside_by_device_get(const Evas *obj, Eo *dev)
{
   EVAS_TYPE_CHECK(obj, EINA_FALSE);
   return efl_canvas_pointer_inside_get(obj, dev);
}

EAPI Eina_List*
evas_objects_at_xy_get(Eo *eo_e, int x, int y, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   EVAS_TYPE_CHECK(eo_e, NULL);
   return _efl_canvas_evas_canvas_objects_at_xy_get_helper(eo_e, efl_data_scope_get(eo_e, EVAS_CANVAS_CLASS), x, y, include_pass_events_objects, include_hidden_objects);
}
/* Internal EO APIs */

EWAPI const Efl_Event_Description _EVAS_CANVAS_EVENT_RENDER_FLUSH_PRE =
   EFL_EVENT_DESCRIPTION("render,flush,pre");
EWAPI const Efl_Event_Description _EVAS_CANVAS_EVENT_RENDER_FLUSH_POST =
   EFL_EVENT_DESCRIPTION("render,flush,post");
EWAPI const Efl_Event_Description _EVAS_CANVAS_EVENT_AXIS_UPDATE =
   EFL_EVENT_DESCRIPTION("axis,update");
EWAPI const Efl_Event_Description _EVAS_CANVAS_EVENT_VIEWPORT_RESIZE =
   EFL_EVENT_DESCRIPTION("viewport,resize");

#define CHECK_ADD(var, ev, member) \
  if ((var) == (ev)) \
    { \
       e->member = EINA_TRUE; \
    }


EOLIAN static Eina_Bool
_evas_canvas_efl_object_event_callback_priority_add(Eo *obj, Evas_Public_Data *e, const Efl_Event_Description *desc, Efl_Callback_Priority priority, Efl_Event_Cb func, const void *user_data)
{
   CHECK_ADD(desc, EVAS_CANVAS_EVENT_RENDER_FLUSH_PRE, cb_render_flush_pre)
   else CHECK_ADD(desc, EVAS_CANVAS_EVENT_RENDER_FLUSH_POST, cb_render_flush_post)
   else CHECK_ADD(desc, EFL_CANVAS_SCENE_EVENT_RENDER_PRE, cb_render_pre)
   else CHECK_ADD(desc, EFL_CANVAS_SCENE_EVENT_RENDER_POST, cb_render_post)

   return efl_event_callback_priority_add(efl_super(obj, MY_CLASS), desc, priority, func, user_data);
}

EOLIAN static Eina_Bool
_evas_canvas_efl_object_event_callback_array_priority_add(Eo *obj, Evas_Public_Data *e, const Efl_Callback_Array_Item *array, Efl_Callback_Priority priority, const void *user_data)
{
   for (int i = 0; array[i].desc; ++i)
     {
        CHECK_ADD(array[i].desc, EVAS_CANVAS_EVENT_RENDER_FLUSH_PRE, cb_render_flush_pre)
        else CHECK_ADD(array[i].desc, EVAS_CANVAS_EVENT_RENDER_FLUSH_POST, cb_render_flush_post)
        else CHECK_ADD(array[i].desc, EFL_CANVAS_SCENE_EVENT_RENDER_PRE, cb_render_pre)
        else CHECK_ADD(array[i].desc, EFL_CANVAS_SCENE_EVENT_RENDER_POST, cb_render_post)
     }
   return efl_event_callback_array_priority_add(efl_super(obj, MY_CLASS), array, priority, user_data);
}
#undef CHECK_ADD
#define EVAS_CANVAS_EXTRA_OPS \
   EFL_OBJECT_OP_FUNC(efl_event_callback_priority_add, _evas_canvas_efl_object_event_callback_priority_add), \
   EFL_OBJECT_OP_FUNC(efl_event_callback_array_priority_add, _evas_canvas_efl_object_event_callback_array_priority_add)


#include "evas_stack.x"
#include "canvas/evas_canvas_eo.c"
#include "efl_canvas_pointer.eo.c"
#include "efl_canvas_scene.eo.c"
