#ifdef HAVE_CONFIG_H
# include "elementary_config.h"
#endif

#define EFL_ACCESS_OBJECT_PROTECTED
#define EFL_ACCESS_COMPONENT_PROTECTED
#define EFL_ACCESS_WIDGET_ACTION_PROTECTED
#define EFL_INPUT_EVENT_PROTECTED
#define EFL_GFX_HINT_PROTECTED
#define EFL_CANVAS_OBJECT_PROTECTED
#define EFL_UI_L10N_PROTECTED
#define EFL_UI_WIN_INLINED_PROTECTED
#define EFL_UI_FOCUS_OBJECT_PROTECTED
#define EFL_UI_WIDGET_FOCUS_MANAGER_PROTECTED
#define EFL_PART_PROTECTED
#define IPA_YLNO_ESU_LANRETNI_MLE

#include <Elementary.h>
#include <Elementary_Cursor.h>

#include "elm_priv.h"
#include "elm_widget_menu.h"
#ifdef HAVE_ELEMENTARY_WL2
# include "ecore_evas_wayland_private.h"
#endif

#include "../evas/canvas/evas_box_eo.h"

#include "elm_part_helper.h"
#include "efl_ui_win_part.eo.h"
#include "elm_plug_eo.h"
#include "efl_ui_win_legacy_eo.h"
#include "efl_ui_win_socket_legacy_eo.h"
#include "efl_ui_win_inlined_legacy_eo.h"
#include "efl_ui_widget_common.h"

#define MY_CLASS EFL_UI_WIN_CLASS
#define MY_CLASS_NAME "Efl.Ui.Win"
#define MY_CLASS_NAME_LEGACY "elm_win"

#define FRAME_OBJ_THEME_MIN_VERSION 119

extern void ecore_evas_dnd_mark_motion_used(Ecore_Evas *ee, unsigned int seat);

Ecore_Evas *_wayland_shm_new(const char *disp_name, Ecore_Window parent, int x, int y, int w, int h, Eina_Bool frame);
Ecore_Evas *_wayland_egl_new(const char *disp_name, Ecore_Window parent, int x, int y, int w, int h, Eina_Bool frame, const int *opt);

static const Elm_Win_Trap *trap = NULL;

static int _paused_windows = 0;

#define TRAP(sd, name, ...)                                             \
  do                                                                    \
    {                                                                   \
       if (sd->type != EFL_UI_WIN_TYPE_FAKE)                                    \
         if ((!trap) || (!trap->name) ||                                \
             ((trap->name) &&                                           \
              (trap->name(sd->trap_data, sd->obj, ## __VA_ARGS__))))    \
           ecore_evas_##name(sd->ee, ##__VA_ARGS__);                    \
    }                                                                   \
  while (0)

#define ELM_WIN_DATA_GET(o, sd) \
  Efl_Ui_Win_Data *sd = efl_data_scope_get(o, MY_CLASS)

#define ELM_WIN_DATA_GET_OR_RETURN(o, ptr, ...)      \
  ELM_WIN_DATA_GET(o, ptr);                          \
  if (!ptr)                                          \
    {                                                \
       ERR("No widget data for object %p (%s)",      \
            o, efl_class_name_get(o));                \
       return __VA_ARGS__;                           \
    }

// Ecore_Evas callbacks are unsafe unlike EO calls. As a consequence a user
// callback (eg evas cb, efl event cb, ...) could be triggered that deletes the
// object. This macro ensures the sd data is still valid after a foreign call.
#define ELM_WIN_DATA_ALIVE_CHECK(_obj, _sd, ...) do { \
   _sd = efl_data_scope_safe_get(_obj, MY_CLASS); \
   if (EINA_UNLIKELY(!(_sd))) { return __VA_ARGS__; } \
   } while (0)

#define ENGINE_GET() (_elm_preferred_engine ? _elm_preferred_engine : _elm_config->engine)

typedef struct _Efl_Ui_Win_Data Efl_Ui_Win_Data;
typedef struct _Input_Pointer_Iterator Input_Pointer_Iterator;

struct _Efl_Ui_Win_Data
{
   Ecore_Evas           *ee;
   Evas                 *evas;
   Evas_Object          *parent; /* parent *window* object*/
   Evas_Object          *img_obj, *frame_obj;
   Eo /* wref */        *bg, *content;
   Evas_Object          *obj; /* The object itself */
   Evas_Object          *indicator;
#ifdef HAVE_ELEMENTARY_X
   struct
   {
      Ecore_X_Window       xwin;
      Ecore_Event_Handler *client_message_handler;
      Ecore_Event_Handler *property_handler;
      Eina_Bool shaped : 1;
   } x;
#endif
#ifdef HAVE_ELEMENTARY_WL2
   struct
   {
      Ecore_Wl2_Window *win;
      Ecore_Event_Handler *configure_handler;
   } wl;
#endif
#ifdef HAVE_ELEMENTARY_COCOA
   struct {
      Ecore_Cocoa_Window *win;
   } cocoa;
#endif
#ifdef HAVE_ELEMENTARY_WIN32
   struct
   {
      Ecore_Win32_Window *win;
      Ecore_Event_Handler *key_down_handler;
   } win32;
#endif

   unsigned /* Efl_Ui_Win_Type */    type;
   Efl_Ui_Win_Keyboard_Mode          kbdmode;
   Efl_Ui_Win_Indicator_Mode         indimode;
   struct
   {
      const char  *info;
      Ecore_Timer *timer;
      int          repeat_count;
      int          shot_counter;
   } shot;
   int                           *autodel_clear, rot;
   struct
   {
      int x, y;
   } screen;
   struct
   {
#ifdef HAVE_ELEMENTARY_WL2
      Ecore_Wl2_Window *win;
      struct wl_surface *surf;
#endif
      Ecore_Evas  *ee;
      Evas_Object *obj;
      int          hot_x, hot_y;
      Eina_Bool    visible : 1;
   } pointer;
   struct
   {
      Evas_Object *fobj; /* focus highlight edje object */

      struct
      {
         Evas_Object *target;
         Eina_Bool    visible : 1;
         Eina_Bool    in_theme: 1; /**< focus highlight is handled by theme.
                                     this is set true if edc data item "focus_highlight" is set to "on" during focus in callback. */
      } cur, prev;

      const char  *style;
      Ecore_Job   *reconf_job;

      Eina_Bool    enabled : 1;
      Eina_Bool    theme_changed : 1; /* set true when the focus theme is changed */
      Eina_Bool    animate : 1; /* set true when the focus highlight animate is enabled */
      Eina_Bool    animate_supported : 1; /* set true when the focus highlight animate is supported by theme */
      Eina_Bool    geometry_changed : 1;
      Eina_Bool    auto_enabled : 1;
      Eina_Bool    auto_animate : 1;
   } focus_highlight;

   Evas_Object *icon;
   const char  *title;
   const char  *icon_name;
   const char  *role;
   const char  *stack_id;
   const char  *stack_master_id;
   Eina_Stringshare *name;
   Eina_Stringshare *accel_pref;

   Eina_Future *finalize_future;

   Evas_Object *main_menu;

   Efl_Ui_Focus_Parent_Provider_Standard *provider;

   struct
   {
      Eina_Stringshare *name; /* Current profile in use */
      Eina_Array       *available; /* Never NULL, contains Eina_Stringshare */
   } profile;
   struct
   {
      int          preferred_rot; /* indicates preferred rotation value, -1 means invalid. */
      int         *rots; /* indicates available rotations */
      unsigned int count; /* number of elements in available rotations */
      Eina_Bool    wm_supported : 1; /* set true when the window manager support window rotation */
      Eina_Bool    use : 1; /* set true when application use window manager rotation. */
   } wm_rot;

   void *trap_data;

   double       aspect; /* defined as w/h or 0 */
   int          size_base_w, size_base_h;
   int          size_step_w, size_step_h;
   int          req_x, req_y, req_w, req_h;
   int          max_w, max_h;
   int          norender;
   int          modal_count;
   int          response;
   int          ignore_frame_resize;
   Eina_Bool    req_wh : 1;
   Eina_Bool    req_xy : 1;
   Eina_Array   *selection_changed;
   Eina_Array   *planned_changes;
   Eina_Inarray *drop_target;

   struct {
      short     pointer_move;
      short     pointer_down;
      short     pointer_up;
      short     pointer_in;
      short     pointer_out;
      short     pointer_cancel;
      short     pointer_wheel;
      short     finger_move;
      short     finger_down;
      short     finger_up;
      short     key_down;
      short     key_up;
      short     render_pre;
      short     render_post;
      short     focus_in;
      short     focus_out;
      short     object_focus_in;
      short     object_focus_out;
      short     device_changed;
   } event_forward;

   struct {
      /* frame_obj is always used except for FAKE */
      Eina_Bool need : 1; /**< if true, application draws its own csd */
      Eina_Bool need_shadow : 1; /**< if true, application draws its csd and shadow */
      Eina_Bool need_borderless : 1;
      Eina_Bool need_bg_solid : 1;
      Eina_Bool need_bg_standard : 1;
      Eina_Bool need_menu : 1;
      Eina_Bool need_unresizable : 1;
      Eina_Bool need_indicator : 1;
      Eina_Bool cur_borderless : 1;
      Eina_Bool cur_shadow : 1;
      Eina_Bool cur_focus : 1;
      Eina_Bool cur_maximized : 1;
      Eina_Bool cur_bg_solid : 1;
      Eina_Bool cur_bg_standard : 1;
      Eina_Bool cur_menu : 1;
      Eina_Bool cur_unresizable : 1;
      Eina_Bool cur_indicator : 1;
      Eina_Bool wayland : 1;
   } csd;

   struct {
      Evas_Object *box, *edje;
      Elm_Win_Indicator_Mode         indmode;
      Elm_Win_Indicator_Opacity_Mode ind_o_mode;
      Eina_Bool    forbidden : 1; /**< Marks some legacy APIs as not allowed. */
      Eina_Bool    bg_must_swallow : 1; /**< Legacy theme compatibility (elm_bg for standard window) */
      Eina_Bool    bg_must_swallow_init : 1;
      Eina_Bool    ctor : 1; /**< legacy constructor: elm_win~add */
   } legacy;
   Efl_Ui_Shared_Win_Data spd;

   Eina_Value exit_on_close;

   Eina_Bool    first_draw : 1;
   Eina_Bool    deferred_resize_job : 1;
   Eina_Bool    urgent : 1;
   Eina_Bool    modal : 1;
   Eina_Bool    demand_attention : 1;
   Eina_Bool    autodel : 1;
   Eina_Bool    autohide : 1;
   Eina_Bool    constrain : 1;
   Eina_Bool    resizing : 1;
   Eina_Bool    minimized : 1;
   Eina_Bool    withdrawn : 1;
   Eina_Bool    sticky : 1;
   Eina_Bool    fullscreen : 1;
   Eina_Bool    maximized : 1;
   Eina_Bool    skip_focus : 1;
   Eina_Bool    floating : 1;
   Eina_Bool    noblank : 1;
   Eina_Bool    theme_alpha : 1; /**< alpha value fetched by a theme. this has higher priority than application_alpha */
   Eina_Bool    application_alpha : 1; /**< alpha value set by an elm_win_alpha_set() api. this has lower priority than theme_alpha */
   Eina_Bool    tmp_updating_hints : 1;
   Eina_Bool    single_edje_content: 1; /* hack for E */
   Eina_Bool    shown : 1;
   Eina_Bool    stack_base : 1;
   Eina_Bool    paused : 1;
};

struct _Input_Pointer_Iterator
{
   Eina_Iterator  iterator;
   Eina_List     *list;
   Eina_Iterator *real_iterator;
   const Eo      *object;
};

static const char SIG_DELETE_REQUEST[] = "delete,request";
static const char SIG_FOCUS_OUT[] = "focus,out"; // deprecated. use "unfocused" instead.
static const char SIG_FOCUS_IN[] = "focus,in"; // deprecated. use "focused" instead.
static const char SIG_MOVED[] = "moved";
static const char SIG_WITHDRAWN[] = "withdrawn";
static const char SIG_MINIMIZED[] = "minimized";
static const char SIG_NORMAL[] = "normal";
static const char SIG_STICK[] = "stick";
static const char SIG_UNSTICK[] = "unstick";
static const char SIG_FULLSCREEN[] = "fullscreen";
static const char SIG_UNFULLSCREEN[] = "unfullscreen";
static const char SIG_MAXIMIZED[] = "maximized";
static const char SIG_UNMAXIMIZED[] = "unmaximized";
static const char SIG_IOERR[] = "ioerr";
static const char SIG_INDICATOR_PROP_CHANGED[] = "indicator,prop,changed";
static const char SIG_ROTATION_CHANGED[] = "rotation,changed";
static const char SIG_PROFILE_CHANGED[] = "profile,changed";
static const char SIG_WM_ROTATION_CHANGED[] = "wm,rotation,changed";

static const Evas_Smart_Cb_Description _smart_callbacks[] = {
   {SIG_DELETE_REQUEST, ""},
   {SIG_FOCUS_OUT, ""},
   {SIG_FOCUS_IN, ""},
   {SIG_MOVED, ""},
   {SIG_WITHDRAWN, ""},
   {SIG_MINIMIZED, ""},
   {SIG_NORMAL, ""},
   {SIG_STICK, ""},
   {SIG_UNSTICK, ""},
   {SIG_FULLSCREEN, ""},
   {SIG_UNFULLSCREEN, ""},
   {SIG_MAXIMIZED, ""},
   {SIG_UNMAXIMIZED, ""},
   {SIG_IOERR, ""},
   {SIG_INDICATOR_PROP_CHANGED, ""},
   {SIG_ROTATION_CHANGED, ""},
   {SIG_PROFILE_CHANGED, ""},
   {SIG_WM_ROTATION_CHANGED, ""},
   {SIG_WIDGET_FOCUSED, ""}, /**< handled by elm_widget */
   {SIG_WIDGET_UNFOCUSED, ""}, /**< handled by elm_widget */
   {NULL, NULL}
};

static Eina_Bool _key_action_return(Evas_Object *obj, const char *params);
static Eina_Bool _key_action_move(Evas_Object *obj, const char *params);

static const Elm_Action key_actions[] = {
   {"return", _key_action_return},
   {"move", _key_action_move},
   {NULL, NULL}
};

Eina_List *_elm_win_list = NULL;
int _elm_win_deferred_free = 0;
static Eina_Value exit_on_all_windows_closed;

static Eina_Bool _elm_win_throttle_ok = EINA_FALSE;
static int _elm_win_count = 0;

static Eina_Bool _elm_win_auto_throttled = EINA_FALSE;

static Eina_Bool _elm_win_state_eval_timer_now = EINA_FALSE;
static Ecore_Timer *_elm_win_state_eval_timer = NULL;

static void _elm_win_legacy_init(Efl_Ui_Win_Data *sd);
static void
_elm_win_on_resize_obj_changed_size_hints(void *data,
                                          Evas *e,
                                          Evas_Object *obj,
                                          void *event_info);
static void
_elm_win_img_callbacks_del(Evas_Object *obj, Evas_Object *imgobj);
static Eina_Error _elm_win_theme_internal(Eo *obj, Efl_Ui_Win_Data *sd);
static void _elm_win_frame_add(Efl_Ui_Win_Data *sd, const char *element, const char *style);
static void _elm_win_frame_style_update(Efl_Ui_Win_Data *sd, Eina_Bool force_emit, Eina_Bool calc);
static inline void _elm_win_need_frame_adjust(Efl_Ui_Win_Data *sd, const char *engine);
static void _elm_win_resize_objects_eval(Evas_Object *obj, Eina_Bool force_resize);
static void _elm_win_frame_obj_update(Efl_Ui_Win_Data *sd, Eina_Bool force);
static void _ee_backbone_init(Efl_Ui_Win *obj, Efl_Ui_Win_Data *pd);
static void _ee_backbone_shutdown(Efl_Ui_Win *obj, Efl_Ui_Win_Data *pd);

static inline Efl_Ui_Win_Type
_elm_win_type_to_efl_ui_win_type(Elm_Win_Type type)
{
   switch (type)
     {
#define CONVERT_TYPE(TYPE) case ELM_WIN_##TYPE: return EFL_UI_WIN_TYPE_##TYPE
      CONVERT_TYPE(BASIC);
      CONVERT_TYPE(DIALOG_BASIC);
      CONVERT_TYPE(DESKTOP);
      CONVERT_TYPE(DOCK);
      CONVERT_TYPE(TOOLBAR);
      CONVERT_TYPE(MENU);
      CONVERT_TYPE(UTILITY);
      CONVERT_TYPE(SPLASH);
      CONVERT_TYPE(DROPDOWN_MENU);
      CONVERT_TYPE(POPUP_MENU);
      CONVERT_TYPE(TOOLTIP);
      CONVERT_TYPE(NOTIFICATION);
      CONVERT_TYPE(COMBO);
      CONVERT_TYPE(DND);
      CONVERT_TYPE(INLINED_IMAGE);
      CONVERT_TYPE(SOCKET_IMAGE);
      CONVERT_TYPE(FAKE);
      CONVERT_TYPE(NAVIFRAME_BASIC);
      default: break;
     }
   return EFL_UI_WIN_TYPE_UNKNOWN;
#undef CONVERT_TYPE
}

static inline Elm_Win_Type
_efl_ui_win_type_to_elm_win_type(Efl_Ui_Win_Type type)
{
   switch (type)
     {
#define CONVERT_TYPE(TYPE) case EFL_UI_WIN_TYPE_##TYPE: return ELM_WIN_##TYPE
      CONVERT_TYPE(BASIC);
      CONVERT_TYPE(DIALOG_BASIC);
      CONVERT_TYPE(DESKTOP);
      CONVERT_TYPE(DOCK);
      CONVERT_TYPE(TOOLBAR);
      CONVERT_TYPE(MENU);
      CONVERT_TYPE(UTILITY);
      CONVERT_TYPE(SPLASH);
      CONVERT_TYPE(DROPDOWN_MENU);
      CONVERT_TYPE(POPUP_MENU);
      CONVERT_TYPE(TOOLTIP);
      CONVERT_TYPE(NOTIFICATION);
      CONVERT_TYPE(COMBO);
      CONVERT_TYPE(DND);
      CONVERT_TYPE(INLINED_IMAGE);
      CONVERT_TYPE(SOCKET_IMAGE);
      CONVERT_TYPE(FAKE);
      CONVERT_TYPE(NAVIFRAME_BASIC);
      default: break;
     }
   return ELM_WIN_UNKNOWN;
#undef CONVERT_TYPE
}

#ifdef HAVE_ELEMENTARY_X
static void _elm_win_xwin_update(Efl_Ui_Win_Data *sd);
#endif

EAPI double _elm_startup_time = 0;

static void
_elm_win_first_frame_do(void *data, Evas *e EINA_UNUSED, void *event_info EINA_UNUSED)
{
   double end = ecore_time_unix_get();
   char *first = data;

   switch (*first)
     {
      case 'A': abort();
      case 'E':
      case 'D': exit(-1);
      case 'T': fprintf(stderr, "Startup time: '%f' - '%f' = '%f' sec\n", end, _elm_startup_time, end - _elm_startup_time);
                break;
     }

   evas_event_callback_del_full(e, EVAS_CALLBACK_RENDER_POST, _elm_win_first_frame_do, data);
}

Ecore_X_Window
_elm_ee_xwin_get(const Ecore_Evas *ee)
{
#ifdef HAVE_ELEMENTARY_X
   const char *engine_name;
   if (!ee) return 0;

   engine_name = ecore_evas_engine_name_get(ee);
   if (EINA_UNLIKELY(!engine_name)) return 0;

   if (!strcmp(engine_name, ELM_SOFTWARE_X11))
     {
        return ecore_evas_software_x11_window_get(ee);
     }
   else if (!strcmp(engine_name, ELM_OPENGL_X11))
     {
        return ecore_evas_gl_x11_window_get(ee);
     }
#else
   (void)ee;
#endif
   return 0;
}

#ifdef HAVE_ELEMENTARY_X
static void
_internal_elm_win_xwindow_get(Efl_Ui_Win_Data *sd)
{
   Ecore_X_Window pwin = sd->x.xwin;
   sd->x.xwin = _elm_ee_xwin_get(sd->ee);
   if (sd->x.xwin != pwin)
     {
        char buf[128];

        snprintf(buf, sizeof(buf), "%x", sd->x.xwin);
        eina_stringshare_del(sd->stack_id);
        sd->stack_id = eina_stringshare_add(buf);
     }
}
#endif

Ecore_Wl2_Window *
_elm_ee_wlwin_get(const Ecore_Evas *ee)
{
#ifdef HAVE_ELEMENTARY_WL2
   const char *engine_name;

   if (!ee) return NULL;

   engine_name = ecore_evas_engine_name_get(ee);
   if (EINA_UNLIKELY(!engine_name)) return NULL;

   if ((!strcmp(engine_name, ELM_WAYLAND_SHM)) ||
       (!strcmp(engine_name, ELM_WAYLAND_EGL)))
     {
        return ecore_evas_wayland2_window_get(ee);
     }
#else
   (void)ee;
#endif
   return NULL;
}

static void
_win_noblank_eval(void)
{
#ifdef HAVE_ELEMENTARY_X
   Eina_List *l;
   Evas_Object *obj;
   int noblanks = 0;
   Eina_Bool change = EINA_FALSE;

   EINA_LIST_FOREACH(_elm_win_list, l, obj)
     {
        ELM_WIN_DATA_GET(obj, sd);

        if (sd->x.xwin)
          {
             _internal_elm_win_xwindow_get(sd);
             if ((sd->noblank) && (!sd->minimized) && (!sd->withdrawn) &&
                 evas_object_visible_get(obj))
               noblanks++;

             change = EINA_TRUE;
          }
     }

   if (!change) return;

   if (noblanks > 0) ecore_x_screensaver_suspend();
   else ecore_x_screensaver_resume();
#endif
#ifdef HAVE_ELEMENTARY_WL2
   // XXX: no wl implementation of this yet - maybe higher up at prop level
#endif
}

static Elm_Process_State _elm_process_state = ELM_PROCESS_STATE_FOREGROUND;

EAPI Elm_Process_State
elm_process_state_get(void)
{
   return _elm_process_state;
}

static void
_elm_win_apply_alpha(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   Eina_Bool enabled;

   if (!sd->ee) return;

   enabled = sd->theme_alpha | sd->application_alpha;
   if (sd->img_obj)
     {
        evas_object_image_alpha_set(sd->img_obj, enabled);
        ecore_evas_alpha_set(sd->ee, enabled);
     }
   else
     {
#ifdef HAVE_ELEMENTARY_X
        if (sd->x.xwin)
          {
             _internal_elm_win_xwindow_get(sd);
             enabled |= (sd->csd.need && !sd->fullscreen);
             if (!ecore_x_screen_is_composited(0))
               {
                  if (enabled || (!sd->x.shaped))
                    TRAP(sd, shaped_set, enabled);
               }
             else
               TRAP(sd, alpha_set, enabled);
          }
        else
#else
          (void)obj;
#endif
          TRAP(sd, alpha_set, enabled);
     }
}

/* auto norender withdrawn is really only for X11.
 * On other backends like wayland, there's actually
 * no way for a client to tell if the window is
 * minimized or not.  You can request minimized state
 * but there's no explicit feedback for minimization
 * or return to normal state.
 *
 * So, blocking drawing based on client side thinking
 * it's minimized, and having the compositor think
 * the client should be drawing will lead to
 * predictably disappointing results.
 *
 * If you maintain a backend that is really capable
 * of handling this properly, feel free to extend
 * the whitelist.
 */
static Eina_Bool
_elm_win_auto_norender_withdrawn(const Evas_Object *obj)
{
   const char *engine;
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);

   if (!sd)
     return _elm_config->auto_norender_withdrawn;

   engine = ecore_evas_engine_name_get(sd->ee);
   if (!strcmp(engine, ELM_SOFTWARE_X11) || !strcmp(engine, ELM_OPENGL_X11))
     return _elm_config->auto_norender_withdrawn;

   return EINA_FALSE;
}


static Eina_Bool
_elm_win_state_eval(void *data EINA_UNUSED)
{
   Eina_List *l;
   Evas_Object *obj;
   int _elm_win_count_shown = 0;
   int _elm_win_count_minimized = 0;
   int _elm_win_count_withdrawn = 0;
   Eina_Bool throttle = EINA_FALSE;

   _elm_win_state_eval_timer_now = EINA_FALSE;
   _elm_win_state_eval_timer = NULL;

   EINA_LIST_FOREACH(_elm_win_list, l, obj)
     {
        if (_elm_win_auto_norender_withdrawn(obj))
          {
             if ((elm_win_withdrawn_get(obj)) ||
                 ((elm_win_iconified_get(obj) &&
                   (_elm_config->auto_norender_iconified_same_as_withdrawn))))
               {
                  if (!evas_object_data_get(obj, "__win_auto_norender"))
                    {
                       Evas *evas = evas_object_evas_get(obj);

                       elm_win_norender_push(obj);
                       evas_object_data_set(obj, "__win_auto_norender", obj);
                       if (_elm_config->auto_flush_withdrawn)
                         {
                            edje_file_cache_flush();
                            edje_collection_cache_flush();
                            evas_image_cache_flush(evas);
                            evas_font_cache_flush(evas);
                         }
                       if (_elm_config->auto_dump_withdrawn)
                         {
                            evas_render_dump(evas);
                         }
                    }

                  if (elm_win_iconified_get(obj))
                    efl_event_callback_call(obj, EFL_UI_WIN_EVENT_PAUSE, NULL);
                  continue;
               }
          }
        if (evas_object_data_get(obj, "__win_auto_norender"))
          {
             elm_win_norender_pop(obj);
             evas_object_data_del(obj, "__win_auto_norender");
          }
     }
   if (((_elm_config->auto_throttle) &&
        (elm_policy_get(ELM_POLICY_THROTTLE) != ELM_POLICY_THROTTLE_NEVER)) ||
        (elm_policy_get(ELM_POLICY_THROTTLE) == ELM_POLICY_THROTTLE_HIDDEN_ALWAYS))
     throttle = EINA_TRUE;
   if (_elm_win_count == 0)
     {
        if ((_elm_win_throttle_ok) && (_elm_win_auto_throttled))
          {
             _elm_process_state = ELM_PROCESS_STATE_FOREGROUND;
             ecore_event_add(ELM_EVENT_PROCESS_FOREGROUND, NULL, NULL, NULL);
             if (throttle)
               ecore_throttle_adjust(-_elm_config->auto_throttle_amount);
             _elm_win_auto_throttled = EINA_FALSE;
          }
     }
   else
     {
        EINA_LIST_FOREACH(_elm_win_list, l, obj)
          {
             if (elm_win_withdrawn_get(obj)) _elm_win_count_withdrawn++;
             else if (elm_win_iconified_get(obj)) _elm_win_count_minimized++;
             else if (evas_object_visible_get(obj)) _elm_win_count_shown++;
          }
        if (_elm_win_count_shown <= 0)
          {
             if ((_elm_win_throttle_ok) && (!_elm_win_auto_throttled))
               {
                  _elm_process_state = ELM_PROCESS_STATE_BACKGROUND;
                  ecore_event_add(ELM_EVENT_PROCESS_BACKGROUND, NULL, NULL, NULL);
                  if (throttle)
                    ecore_throttle_adjust(_elm_config->auto_throttle_amount);
                  _elm_win_auto_throttled = EINA_TRUE;
               }
          }
        else
          {
             if ((_elm_win_throttle_ok) && (_elm_win_auto_throttled))
               {
                  _elm_process_state = ELM_PROCESS_STATE_FOREGROUND;
                  ecore_event_add(ELM_EVENT_PROCESS_FOREGROUND, NULL, NULL, NULL);
                  if (throttle)
                    ecore_throttle_adjust(-_elm_config->auto_throttle_amount);
                  _elm_win_auto_throttled = EINA_FALSE;
               }
          }
     }
   _win_noblank_eval();
   return EINA_FALSE;
}

static Eina_Bool
_elm_win_policy_quit_triggered(Eo* triggering_obj)
{
   if (elm_policy_get(ELM_POLICY_QUIT) == ELM_POLICY_QUIT_LAST_WINDOW_HIDDEN)
     {
        Eina_List *l;
        Evas_Object *win;

        EINA_LIST_FOREACH(_elm_win_list, l, win)
          if (win != triggering_obj && evas_object_visible_get(win) == EINA_TRUE)
            {
               return EINA_FALSE;
            }
        return EINA_TRUE;
     }

   return EINA_FALSE;
}

static void
_elm_win_flush_cache_and_exit(Eo *obj)
{
   edje_file_cache_flush();
   edje_collection_cache_flush();
   evas_image_cache_flush(evas_object_evas_get(obj));
   evas_font_cache_flush(evas_object_evas_get(obj));
   elm_exit();
}

static void
_elm_win_state_eval_queue(Eina_Bool now)
{
   if ((_elm_win_state_eval_timer_now) && (_elm_win_state_eval_timer)) return;
   if (_elm_win_state_eval_timer) ecore_timer_del(_elm_win_state_eval_timer);
   if (now)
     _elm_win_state_eval_timer = ecore_timer_add(0.0, _elm_win_state_eval, NULL);
   else
     _elm_win_state_eval_timer = ecore_timer_add(0.5, _elm_win_state_eval, NULL);
   _elm_win_state_eval_timer_now = now;
}

// example shot spec (wait 0.1 sec then save as my-window.png):
// ELM_ENGINE="shot:delay=0.1:file=my-window.png"

static double
_shot_delay_get(Efl_Ui_Win_Data *sd)
{
   char *p, *pd;
   char *d = strdup(sd->shot.info);

   if (!d) return 0.5;
   for (p = (char *)sd->shot.info; *p; p++)
     {
        if (!strncmp(p, "delay=", 6))
          {
             double v;

             for (pd = d, p += 6; (*p) && (*p != ':'); p++, pd++)
               {
                  *pd = *p;
               }
             *pd = 0;
             v = _elm_atof(d);
             free(d);
             return v;
          }
     }
   free(d);

   return 0.5;
}

static char *
_shot_file_get(Efl_Ui_Win_Data *sd)
{
   char *p;
   char *tmp = strdup(sd->shot.info);
   char *repname = NULL;

   if (!tmp) return NULL;

   for (p = (char *)sd->shot.info; *p; p++)
     {
        if (!strncmp(p, "file=", 5))
          {
             strcpy(tmp, p + 5);
             if (!sd->shot.repeat_count) return tmp;
             else
               {
                  char *dotptr = strrchr(tmp, '.');
                  if (dotptr)
                    {
                       size_t size = sizeof(char) * (strlen(tmp) + 16);
                       repname = malloc(size);
                       if (repname)
                         {
                            strncpy(repname, tmp, dotptr - tmp);
                            snprintf(repname + (dotptr - tmp), size -
                                     (dotptr - tmp), "%03i",
                                     sd->shot.shot_counter + 1);
                            strcat(repname, dotptr);
                         }
                       free(tmp);
                       return repname;
                    }
               }
          }
     }
   free(tmp);
   if (!sd->shot.repeat_count) return strdup("out.png");

   repname = malloc(sizeof(char) * 24);
   if (!repname) return NULL;
   snprintf(repname, sizeof(char) * 24, "out%03i.png",
            sd->shot.shot_counter + 1);

   return repname;
}

static int
_shot_repeat_count_get(Efl_Ui_Win_Data *sd)
{
   char *p, *pd;
   char *d = strdup(sd->shot.info);

   if (!d) return 0;
   for (p = (char *)sd->shot.info; *p; p++)
     {
        if (!strncmp(p, "repeat=", 7))
          {
             int v;

             for (pd = d, p += 7; (*p) && (*p != ':'); p++, pd++)
               {
                  *pd = *p;
               }
             *pd = 0;
             v = atoi(d);
             if (v < 0) v = 0;
             if (v > 1000) v = 999;
             free(d);
             return v;
          }
     }
   free(d);

   return 0;
}

static char *
_shot_key_get(Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   return NULL;
}

static char *
_shot_flags_get(Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   return NULL;
}

static void
_shot_do(Efl_Ui_Win_Data *sd)
{
   Ecore_Evas *ee;
   Evas_Object *o;
   unsigned int *pixels;
   int w, h;
   char *file, *key, *flags;

   ecore_evas_manual_render(sd->ee);
   pixels = (void *)ecore_evas_buffer_pixels_get(sd->ee);
   if (!pixels) return;

   ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h);
   if ((w < 1) || (h < 1)) return;

   file = _shot_file_get(sd);
   if (!file) return;

   key = _shot_key_get(sd);
   flags = _shot_flags_get(sd);
   ee = ecore_evas_buffer_new(1, 1);
   o = evas_object_image_add(ecore_evas_get(ee));
   evas_object_image_alpha_set(o,
                               sd->theme_alpha | sd->application_alpha);
   evas_object_image_size_set(o, w, h);
   evas_object_image_data_set(o, pixels);
   if (!evas_object_image_save(o, file, key, flags))
     {
        ERR("Cannot save window to '%s' (key '%s', flags '%s')",
            file, key, flags);
     }
   free(file);
   free(key);
   free(flags);
   ecore_evas_free(ee);
   if (sd->shot.repeat_count) sd->shot.shot_counter++;
}

static Eina_Bool
_shot_delay(void *data)
{
   ELM_WIN_DATA_GET(data, sd);

   _shot_do(sd);
   if (sd->shot.repeat_count)
     {
        int remainshot = (sd->shot.repeat_count - sd->shot.shot_counter);
        if (remainshot > 0) return EINA_TRUE;
     }
   sd->shot.timer = NULL;
   elm_exit();

   return EINA_FALSE;
}

static void
_shot_init(Efl_Ui_Win_Data *sd)
{
   if (!sd->shot.info) return;

   sd->shot.repeat_count = _shot_repeat_count_get(sd);
   sd->shot.shot_counter = 0;
}

static void
_shot_handle(Efl_Ui_Win_Data *sd)
{
   if (!sd->shot.info) return;

   if (!sd->shot.timer)
     sd->shot.timer = ecore_timer_add(_shot_delay_get(sd), _shot_delay,
                                      sd->obj);
}

/* elm-win specific associate, does the trap while ecore_evas_object_associate()
 * does not.
 */
static Efl_Ui_Win_Data *
_elm_win_associate_get(const Ecore_Evas *ee)
{
   Evas_Object *obj = ecore_evas_data_get(ee, "elm_win");
   return efl_data_scope_safe_get(obj, MY_CLASS);
}

/* Interceptors Callbacks */
static void
_elm_win_obj_intercept_raise(void *data, Evas_Object *obj EINA_UNUSED)
{
   // Note: This is probably not necessary anymore (Win implements raise)
   ELM_WIN_DATA_GET(data, sd);
   TRAP(sd, raise);
}

static void
_elm_win_obj_intercept_lower(void *data, Evas_Object *obj EINA_UNUSED)
{
   // Note: This is probably not necessary anymore (Win ignores lower)
   ELM_WIN_DATA_GET(data, sd);
   TRAP(sd, lower);
}

static void
_elm_win_obj_intercept_stack_above(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, Evas_Object *above EINA_UNUSED)
{
   INF("TODO: %s", __func__);
}

static void
_elm_win_obj_intercept_stack_below(void *data EINA_UNUSED, Evas_Object *obj EINA_UNUSED, Evas_Object *below EINA_UNUSED)
{
   INF("TODO: %s", __func__);
}

static void
_elm_win_obj_intercept_layer_set(void *data, Evas_Object *obj EINA_UNUSED, int l)
{
   ELM_WIN_DATA_GET(data, sd);
   TRAP(sd, layer_set, l);
}

/* Event Callbacks */

static void
_elm_win_size_hints_update(Efl_Ui_Win *win, Efl_Ui_Win_Data *sd)
{
   Eina_Size2D min, max;

   min = efl_gfx_hint_size_combined_min_get(win);
   max = efl_gfx_hint_size_combined_max_get(win);
   if (max.w < 1) max.w = -1;
   if (max.h < 1) max.h = -1;

   TRAP(sd, size_min_set, min.w, min.h);
   TRAP(sd, size_max_set, max.w, max.h);
}

static void
_elm_win_obj_callback_changed_size_hints(void *data EINA_UNUSED, Evas *e EINA_UNUSED,
                                         Evas_Object *obj, void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(obj, sd);

   if (sd->tmp_updating_hints) return;
   _elm_win_size_hints_update(obj, sd);
}
/* end of elm-win specific associate */

static void
_elm_win_move(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   int x, y;
   Eo *obj;
   Eina_Position2D pos;

   if (!sd) return;
   obj = sd->obj;

   ecore_evas_geometry_get(ee, &x, &y, NULL, NULL);
   pos.x = sd->screen.x = x;
   pos.y = sd->screen.y = y;
   efl_event_callback_call(sd->obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, &pos);
   evas_object_smart_callback_call(sd->obj, "move", NULL);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   evas_nochange_push(evas_object_evas_get(sd->obj));
   sd->response++;
   sd->req_xy = EINA_FALSE;
   evas_object_move(sd->obj, x, y);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   sd->response--;
   evas_nochange_pop(evas_object_evas_get(sd->obj));
}

static void
_elm_win_resize_job(void *data)
{
   ELM_WIN_DATA_GET(data, sd);
   int w, h;

   sd->deferred_resize_job = EINA_FALSE;
   ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h);
   if (sd->constrain)
     {
        int sw, sh;
        ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sw, &sh);
        w = MIN(w, sw);
        h = MIN(h, sh);
     }

   if (sd->frame_obj)
     {
        int fx, fy, fw, fh;

        evas_output_framespace_get(sd->evas, &fx, &fy, &fw, &fh);
        evas_object_geometry_set(sd->frame_obj, -fx, -fy, w + fw, h + fh);
     }

   if (sd->main_menu)
     {
        Eina_Position2D pos;

        pos = efl_gfx_entity_position_get(sd->main_menu);
        elm_menu_move(sd->main_menu, pos.x, pos.y);
     }

   sd->response++;
   sd->req_wh = EINA_FALSE;
   evas_object_resize(sd->obj, w, h);
   evas_object_resize(sd->legacy.edje, w, h);
   sd->response--;
}

static void
_elm_win_pre_render(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   Eo *obj;

   if (!sd) return;
   obj = sd->obj;

   _elm_win_throttle_ok = EINA_TRUE;
   if (!sd->first_draw)
     {
        int mw, mh;

        if (sd->type != EFL_UI_WIN_TYPE_FAKE)
          {
             edje_object_thaw(sd->frame_obj);
             evas_object_show(sd->frame_obj);
          }

        _elm_win_frame_style_update(sd, 1, 1);
        ELM_WIN_DATA_ALIVE_CHECK(obj, sd);

        if (sd->frame_obj)
          {
             /* force initial sizing on frame to enable sizing of content */
             edje_object_size_min_calc(sd->frame_obj, &mw, &mh);
             evas_object_resize(sd->frame_obj, mw, mh);
          }

        if (sd->img_obj)
          {
             evas_object_show(sd->img_obj);
          }
        if (sd->pointer.obj) evas_object_show(sd->pointer.obj);
#ifdef ELEMENTARY_X
        if (sd->type == ELM_WIN_TOOLTIP)
          {
             _internal_elm_win_xwindow_get(sd);
             ecore_x_window_shape_input_rectangle_set(sd->x.xwin, 0, 0, 0, 0);
          }
#endif
        sd->first_draw = EINA_TRUE;
        /* set this to handle ecore-evas engine code which incorrectly
         * assumes that a client resize call is the same as a server resize
         * event, or which has no server event
         */
        sd->deferred_resize_job = EINA_TRUE;
     }
   if (sd->deferred_resize_job)
     {
        _elm_win_resize_job(sd->obj);
        _elm_win_frame_obj_update(sd, 1);
     }
}

static void
_elm_win_resize(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   if (!sd) return;

   sd->deferred_resize_job = EINA_TRUE;
}

static void
_elm_win_mouse_in(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   if (!sd) return;

   _elm_win_throttle_ok = EINA_TRUE;
   sd->resizing = EINA_FALSE;
#ifdef HAVE_ELEMENTARY_WL2
   if ((sd->wl.win) && (sd->pointer.ee))
     {
        sd->pointer.visible = EINA_TRUE;
        sd->pointer.surf = ecore_wl2_window_surface_get(sd->pointer.win);
        _elm_win_wl_cursor_set(sd->obj, NULL);
        //ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
     }
#endif
}

static void
_elm_win_mouse_out(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_WL2
   if ((sd->wl.win) && (sd->pointer.ee))
     sd->pointer.visible = EINA_FALSE;
#endif
}

static void
_elm_win_focus_highlight_reconfigure_job_stop(Efl_Ui_Win_Data *sd)
{
   ELM_SAFE_FREE(sd->focus_highlight.reconf_job, ecore_job_del);
}

static void
_elm_win_focus_highlight_visible_set(Efl_Ui_Win_Data *sd,
                                     Eina_Bool visible)
{
   Evas_Object *fobj = sd->focus_highlight.fobj;
   if (!fobj) return;

   if (visible)
     {
        evas_object_show(fobj);
        if (elm_widget_is_legacy(sd->obj))
          edje_object_signal_emit(fobj, "elm,action,focus,show", "elm");
        else
          edje_object_signal_emit(fobj, "efl,focus,visible,on", "efl");
     }
   else
     {
        if (elm_widget_is_legacy(sd->obj))
          edje_object_signal_emit(fobj, "elm,action,focus,hide", "elm");
        else
          edje_object_signal_emit(fobj, "efl,focus,visible,off", "efl");
     }
}

Evas_Object *
_elm_win_focus_highlight_object_get(Evas_Object *obj)
{
   ELM_WIN_DATA_GET(obj, sd);

   return sd->focus_highlight.fobj;
}

static void
_elm_win_focus_highlight_anim_setup(Efl_Ui_Win_Data *sd,
                                    Evas_Object *obj)
{
   Eina_Rect rt, rp;
   Edje_Message_Int_Set *m;
   Evas_Object *target = sd->focus_highlight.cur.target;

   rp = efl_gfx_entity_geometry_get(obj);
   rt = elm_widget_focus_highlight_geometry_get(target);
   efl_gfx_entity_geometry_set(obj, rt);

   if (eina_rectangle_equal(&rp.rect, &rt.rect)) return;

   if (!_elm_config->focus_highlight_clip_disable)
     evas_object_clip_unset(obj);

   m = alloca(sizeof(*m) + (sizeof(int) * 8));
   m->count = 8;
   m->val[0] = rp.x - rt.x;
   m->val[1] = rp.y - rt.y;
   m->val[2] = rp.w;
   m->val[3] = rp.h;
   m->val[4] = 0;
   m->val[5] = 0;
   m->val[6] = rt.w;
   m->val[7] = rt.h;
   edje_object_message_send(obj, EDJE_MESSAGE_INT_SET, 1, m);
}

static void
_elm_win_focus_highlight_simple_setup(Efl_Ui_Win_Data *sd,
                                      Evas_Object *obj)
{
   Evas_Object *clip, *target = sd->focus_highlight.cur.target;

   efl_gfx_entity_geometry_set(obj, elm_widget_focus_highlight_geometry_get(target));

   if (!_elm_config->focus_highlight_clip_disable)
     {
        clip = evas_object_clip_get(target);
        if (clip) evas_object_clip_set(obj, clip);
     }

   if (elm_widget_is_legacy(sd->obj))
     edje_object_signal_emit(obj, "elm,state,anim,stop", "elm");
   else
     edje_object_signal_emit(obj, "efl,state,animating,stopped", "efl");
}

static void
_elm_win_focus_prev_target_del(void *data,
                               Evas *e EINA_UNUSED,
                               Evas_Object *obj EINA_UNUSED,
                               void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);
   sd->focus_highlight.prev.target = NULL;
}

static void
_elm_win_focus_highlight_reconfigure_job(void *data)
{
   ELM_WIN_DATA_GET(data, sd);
   Evas_Object *target = sd->focus_highlight.cur.target;
   Evas_Object *previous = sd->focus_highlight.prev.target;
   Evas_Object *fobj = sd->focus_highlight.fobj;
   Eina_Bool visible_changed;
   Eina_Bool common_visible;
   const char *sig = NULL;

   _elm_win_focus_highlight_reconfigure_job_stop(sd);

   visible_changed = (sd->focus_highlight.cur.visible !=
                      sd->focus_highlight.prev.visible);

   if ((target == previous) && (!visible_changed) &&
       (!sd->focus_highlight.geometry_changed) &&
       (!sd->focus_highlight.theme_changed))
     return;

   if (previous)
     {
        evas_object_event_callback_del_full
           (previous, EVAS_CALLBACK_DEL, _elm_win_focus_prev_target_del, data);
        if (sd->focus_highlight.prev.in_theme)
          {
             if (elm_widget_is_legacy(sd->obj))
               elm_widget_signal_emit
                  (previous, "elm,action,focus_highlight,hide", "elm");
             else
               elm_widget_signal_emit
                  (previous, "efl,action,focus_highlight,hide", "efl");
          }
     }

   if (!target)
     common_visible = EINA_FALSE;
   else if (sd->focus_highlight.cur.in_theme)
     {
        common_visible = EINA_FALSE;

        if (elm_widget_is_legacy(sd->obj))
          {
             if (sd->focus_highlight.cur.visible)
               sig = "elm,action,focus_highlight,show";
             else
               sig = "elm,action,focus_highlight,hide";
          }
        else
          {
             if (sd->focus_highlight.cur.visible)
               sig = "efl,action,focus_highlight,show";
             else
               sig = "efl,action,focus_highlight,hide";
          }
     }
   else
     common_visible = sd->focus_highlight.cur.visible;

   if (sig)
     {
        if (elm_widget_is_legacy(sd->obj))
          elm_widget_signal_emit(target, sig, "elm");
        else
          elm_widget_signal_emit(target, sig, "efl");
     }

   if ((!target) || (!common_visible) || (sd->focus_highlight.cur.in_theme))
     {
        if (target)
          _elm_win_focus_highlight_simple_setup(sd, fobj);
        goto the_end;
     }

   if (sd->focus_highlight.theme_changed)
     {
        const char *str;

       if (sd->focus_highlight.style)
          str = sd->focus_highlight.style;
        else
          str = "default";

        elm_widget_theme_object_set
          (sd->obj, fobj, "focus_highlight", "top", str);
        sd->focus_highlight.theme_changed = EINA_FALSE;

        if ((sd->focus_highlight.animate) || (sd->focus_highlight.auto_animate))
          {
             str = edje_object_data_get(sd->focus_highlight.fobj, "animate");
             sd->focus_highlight.animate_supported = ((str) && (!strcmp(str, "on")));
          }
        else
          sd->focus_highlight.animate_supported = EINA_FALSE;
     }

   if ((sd->focus_highlight.animate_supported) && (previous) &&
       (!sd->focus_highlight.prev.in_theme))
     _elm_win_focus_highlight_anim_setup(sd, fobj);
   else
     _elm_win_focus_highlight_simple_setup(sd, fobj);
   evas_object_raise(fobj);

the_end:
   _elm_win_focus_highlight_visible_set(sd, common_visible);
   sd->focus_highlight.geometry_changed = EINA_FALSE;
   sd->focus_highlight.prev = sd->focus_highlight.cur;
   if (sd->focus_highlight.prev.target)
     {
        evas_object_event_callback_add
              (sd->focus_highlight.prev.target,
               EVAS_CALLBACK_DEL, _elm_win_focus_prev_target_del, data);
     }
}

static void
_elm_win_focus_highlight_reconfigure_job_start(Efl_Ui_Win_Data *sd)
{
   ecore_job_del(sd->focus_highlight.reconf_job);

   sd->focus_highlight.reconf_job = ecore_job_add(
       _elm_win_focus_highlight_reconfigure_job, sd->obj);
}

static void
_elm_win_focus_in(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   Evas_Object *obj;

   if ((!sd) || (sd->modal_count)) return;

   _elm_win_throttle_ok = EINA_TRUE;
   obj = sd->obj;

   _elm_widget_top_win_focused_set(obj, EINA_TRUE);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   if (sd->type != EFL_UI_WIN_TYPE_FAKE)
     {
        Efl_Ui_Focus_Manager *man = sd->obj;
        while(efl_ui_focus_manager_redirect_get(man))
          {
             man = efl_ui_focus_manager_redirect_get(man);
          }

        Evas_Object *focused = efl_ui_focus_manager_focus_get(man);
        if (focused)
          efl_ui_focus_object_focus_set(focused, EINA_TRUE);
     }

   evas_object_smart_callback_call(obj, SIG_FOCUS_IN, NULL);
   evas_object_smart_callback_call(obj, SIG_WIDGET_FOCUSED, NULL);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   sd->focus_highlight.cur.visible = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
   _elm_win_frame_style_update(sd, 0, 1);

   if (_elm_config->atspi_mode)
     {
        efl_access_window_activated_signal_emit(obj);
        efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_ACTIVE, EINA_TRUE);
     }

   /* do nothing */
   /* else if (sd->img_obj) */
   /*   { */
   /*   } */
   if ((!efl_ui_focus_manager_focus_get(sd->obj)) &&
       (!efl_ui_focus_manager_redirect_get(sd->obj)))
     {
        Efl_Ui_Focus_Object *child;

        child = efl_ui_focus_manager_request_subchild(sd->obj, sd->obj);

        if (child)
          efl_ui_focus_manager_focus_set(sd->obj, sd->obj);
        else  if (!evas_focus_get(evas_object_evas_get(sd->obj)))
          evas_object_focus_set(obj, EINA_TRUE);
     }
}

static void
_elm_win_focus_out(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   Evas_Object *obj;

   if (!sd) return;

   obj = sd->obj;

   _elm_widget_top_win_focused_set(obj, EINA_FALSE);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   evas_object_smart_callback_call(obj, SIG_FOCUS_OUT, NULL);
   evas_object_smart_callback_call(obj, SIG_WIDGET_UNFOCUSED, NULL);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   sd->focus_highlight.cur.visible = EINA_FALSE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
   if (!sd->resizing)
     _elm_win_frame_style_update(sd, 0, 1);

   /* access */
   _elm_access_object_highlight_disable(evas_object_evas_get(obj));

   if (_elm_config->atspi_mode)
     {
        efl_access_window_deactivated_signal_emit(obj);
        efl_access_state_changed_signal_emit(obj, EFL_ACCESS_STATE_TYPE_ACTIVE, EINA_FALSE);
     }

   if (sd->type != EFL_UI_WIN_TYPE_FAKE)
     {
        Efl_Ui_Focus_Manager *man = sd->obj;
        while(efl_ui_focus_manager_redirect_get(man))
          {
             man = efl_ui_focus_manager_redirect_get(man);
          }

        Evas_Object *focused = efl_ui_focus_manager_focus_get(man);
        efl_ui_focus_object_focus_set(focused, EINA_FALSE);
     }
   /* do nothing */
   /* if (sd->img_obj) */
   /*   { */
   /*   } */
}

static void
_elm_win_available_profiles_del(Efl_Ui_Win_Data *sd)
{
   Eina_Stringshare *prof;
   Eina_Iterator *it;

   it = eina_array_iterator_new(sd->profile.available);
   EINA_ITERATOR_FOREACH(it, prof)
     eina_stringshare_del(prof);
   eina_iterator_free(it);
   eina_array_flush(sd->profile.available);
}

static void
_elm_win_profile_del(Efl_Ui_Win_Data *sd)
{
   ELM_SAFE_FREE(sd->profile.name, eina_stringshare_del);
}

static Eina_Bool
_internal_elm_win_profile_set(Efl_Ui_Win_Data *sd, const char *profile)
{
   Eina_Bool changed = EINA_FALSE;

   if (profile == sd->profile.name) return EINA_FALSE;
   if (profile)
     {
        if (eina_stringshare_replace(&sd->profile.name, profile))
          changed = EINA_TRUE;
     }
   else
     _elm_win_profile_del(sd);

   return changed;
}

static inline Eina_Bool
_profile_exists(Efl_Ui_Win_Data *sd, const char *profile)
{
   Eina_Bool found = EINA_FALSE;
   Eina_Stringshare *prof;
   Eina_Iterator *it;

   if (!profile) return EINA_FALSE;
   it = eina_array_iterator_new(sd->profile.available);
   EINA_ITERATOR_FOREACH(it, prof)
     if (!strcmp(profile, prof))
       {
          found = EINA_TRUE;
          break;
       }
   eina_iterator_free(it);
   return found;
}

static void
_elm_win_profile_update(Efl_Ui_Win_Data *sd)
{
   if (getenv("ELM_PROFILE")) return;

   if (eina_array_count(sd->profile.available))
     {
        Eina_Bool found = _profile_exists(sd, sd->profile.name);

        /* If current profile is not present in an available profiles,
         * change current profile to the 1st element of an array.
         */
        if (!found)
          _internal_elm_win_profile_set(sd, eina_array_data_get(sd->profile.available, 0));
     }

   _config_profile_lock = EINA_TRUE;
   _elm_config_profile_set(sd->profile.name);

   /* update sub ee */
   Ecore_Evas *ee2;
   Eina_List *sub, *l = NULL;

   sub = ecore_evas_sub_ecore_evas_list_get(sd->ee);
   EINA_LIST_FOREACH(sub, l, ee2)
     ecore_evas_window_profile_set(ee2, sd->profile.name);

   efl_event_callback_legacy_call(sd->obj, EFL_UI_WIN_EVENT_PROFILE_CHANGED, NULL);
}

static inline void
_elm_win_frame_geometry_adjust(Efl_Ui_Win_Data *sd)
{
   int l = 0, t = 0, r = 0, b = 0;

   if (sd->frame_obj && sd->csd.need && !sd->fullscreen)
     {
        int fw, fh, ox, oy, ow, oh;
        evas_object_geometry_get(sd->frame_obj, NULL, NULL, &fw, &fh);
        sd->ignore_frame_resize++;
        evas_object_resize(sd->frame_obj, 1000, 1000);
        if (elm_widget_is_legacy(sd->obj))
          edje_object_part_geometry_get(sd->frame_obj, "elm.spacer.opaque",
                                        &ox, &oy, &ow, &oh);
        else
          edje_object_part_geometry_get(sd->frame_obj, "efl.spacer.opaque",
                                        &ox, &oy, &ow, &oh);
        evas_object_resize(sd->frame_obj, fw, fh);
        sd->ignore_frame_resize--;
        fw = 1000; fh = 1000;
        l = ox;
        t = oy;
        r = fw - ow - l;
        b = fh - oh - t;
        if (l < 0) l = 0;
        if (r < 0) r = 0;
        if (t < 0) t = 0;
        if (b < 0) b = 0;
     }
   ecore_evas_shadow_geometry_set(sd->ee, l, r, t, b);
}

static inline Eina_Bool
_elm_win_framespace_set(Efl_Ui_Win_Data *sd, int x, int y, int w, int h)
{
   int fx, fy, fw, fh;

   evas_output_framespace_get(sd->evas, &fx, &fy, &fw, &fh);
   evas_output_framespace_set(sd->evas, x, y, w, h);

   // return true if framespace geometry changed
   return ((fx != x) || (fy != y) || (fw != w) || (fh != h));
}

static void
_elm_win_frame_obj_update(Efl_Ui_Win_Data *sd, Eina_Bool force)
{
   int ox, oy, ow, oh;
   int cx, cy, cw, ch;
   int w, h;
   int l, r, t, b;

   if (!sd->frame_obj) return;
   if (!sd->csd.need) return;
   _elm_win_frame_geometry_adjust(sd);
   ecore_evas_shadow_geometry_get(sd->ee, &l, &r, &t, &b);
   sd->ignore_frame_resize++;
   evas_object_geometry_get(sd->frame_obj, &ox, &oy, &ow, &oh);
   evas_object_resize(sd->frame_obj, 1000, 1000);
   if (elm_widget_is_legacy(sd->obj))
     edje_object_part_geometry_get(sd->frame_obj, "elm.spacer.content", &cx, &cy, &cw, &ch);
   else
     edje_object_part_geometry_get(sd->frame_obj, "efl.spacer.content", &cx, &cy, &cw, &ch);
   evas_object_resize(sd->frame_obj, ow, oh);
   sd->ignore_frame_resize--;
   if (!_elm_win_framespace_set(sd, cx, cy, 1000 - cw, 1000 - ch) && (!force)) return;
   _elm_win_frame_geometry_adjust(sd);

   if (!sd->first_draw) return;

   evas_object_geometry_get(sd->obj, NULL, NULL, &w, &h);
   if (w && h)
     {
        TRAP(sd, resize, w, h);
     }
}

static int
_win_rotation_degree_check(int rotation)
{
   if ((rotation > 360) || (rotation < 0))
     {
        WRN("Rotation degree should be 0 ~ 360 (passed degree: %d)", rotation);
        rotation %= 360;
        if (rotation < 0) rotation += 360;
     }
   return rotation;
}

/*
 * This API resizes the internal window(ex: X window) and evas_output.
 * But this does not resize the elm window object and its contents.
 */
static void
_win_rotate(Evas_Object *obj, Efl_Ui_Win_Data *sd, int rotation, Eina_Bool resize)
{
   rotation = _win_rotation_degree_check(rotation);
   if (sd->rot == rotation) return;
   sd->rot = rotation;
   if (resize) TRAP(sd, rotation_with_resize_set, rotation);
   else TRAP(sd, rotation_set, rotation);
   efl_gfx_hint_size_restricted_min_set(obj, EINA_SIZE2D(-1, -1));
   efl_gfx_hint_size_restricted_max_set(obj, EINA_SIZE2D(-1, -1));
   _elm_win_resize_objects_eval(obj, EINA_FALSE);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
   _elm_win_frame_obj_update(sd, 0);
   efl_event_callback_call
     (obj, EFL_UI_WIN_EVENT_WIN_ROTATION_CHANGED, &rotation);
   evas_object_smart_callback_call(obj, "rotation,changed", NULL);
   if (_elm_config->atspi_mode)
     {
        Evas_Coord x = 0, y = 0, width = 0, height = 0;
        elm_win_screen_size_get(obj, &x, &y, &width, &height);
        if ((sd->rot == 0) || (sd->rot == 180))
          {
             efl_access_bounds_changed_signal_emit(obj, x, y, width, height);
          }
        else
          {
             efl_access_bounds_changed_signal_emit(obj, x, y, height, width);
          }
     }
}

EOLIAN static void
_efl_ui_win_win_rotation_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd, int rotation)
{
   Efl_Ui_Widget *widget;
   Eina_Iterator *it;
   int rot = rotation %360;

   if (pd->rot == rot) return;

   _win_rotate(obj, pd, rot, EINA_FALSE);

   it = efl_ui_widget_tree_widget_iterator(obj);
   EINA_ITERATOR_FOREACH(it, widget)
     {
        if (!efl_isa(widget, EFL_UI_LAYOUT_BASE_CLASS)) continue;

        if (efl_ui_layout_automatic_theme_rotation_get(widget))
          efl_ui_layout_theme_rotation_apply(widget, rot);
     }
}

EOLIAN static int
_efl_ui_win_win_rotation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd)
{
   return pd->rot;
}

EAPI void
elm_win_rotation_set(Evas_Object *obj, int rotation)
{
   efl_ui_win_rotation_set(obj, rotation);
}

EAPI int
elm_win_rotation_get(const Evas_Object *obj)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, -1);
   return efl_ui_win_rotation_get(obj);
}

static void
_elm_win_state_change(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   Evas_Object *obj;
   Eina_Bool ch_withdrawn = EINA_FALSE;
   Eina_Bool ch_sticky = EINA_FALSE;
   Eina_Bool ch_minimized = EINA_FALSE;
   Eina_Bool ch_fullscreen = EINA_FALSE;
   Eina_Bool ch_maximized = EINA_FALSE;
   Eina_Bool ch_profile = EINA_FALSE;
   Eina_Bool ch_wm_rotation = EINA_FALSE;
   const char *profile;

   if (!sd) return;

   obj = sd->obj;

   if (sd->withdrawn != ecore_evas_withdrawn_get(sd->ee))
     {
        sd->withdrawn = ecore_evas_withdrawn_get(sd->ee);
        ch_withdrawn = EINA_TRUE;
     }
   if (sd->sticky != ecore_evas_sticky_get(sd->ee))
     {
        sd->sticky = ecore_evas_sticky_get(sd->ee);
        ch_sticky = EINA_TRUE;
     }
   if (sd->minimized != ecore_evas_iconified_get(sd->ee))
     {
        sd->minimized = ecore_evas_iconified_get(sd->ee);
        ch_minimized = EINA_TRUE;
     }
   if (sd->fullscreen != ecore_evas_fullscreen_get(sd->ee))
     {
        sd->fullscreen = ecore_evas_fullscreen_get(sd->ee);
        ch_fullscreen = EINA_TRUE;
     }
   if (sd->maximized != ecore_evas_maximized_get(sd->ee))
     {
        sd->maximized = ecore_evas_maximized_get(sd->ee);
        ch_maximized = EINA_TRUE;
     }

   if (ecore_evas_window_profile_supported_get(sd->ee))
     {
        profile = ecore_evas_window_profile_get(sd->ee);
        ch_profile = _internal_elm_win_profile_set(sd, profile);
     }

   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   if (sd->wm_rot.use)
     {
        if (sd->rot != ecore_evas_rotation_get(sd->ee))
          {
             ch_wm_rotation = EINA_TRUE;
          }
     }

   _elm_win_state_eval_queue(EINA_TRUE);

   if ((ch_withdrawn) || (ch_minimized))
     {
        ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
        if (sd->withdrawn)
          efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_WITHDRAWN, NULL);
        else if (sd->minimized)
          {
             efl_event_callback_call(obj, EFL_UI_WIN_EVENT_MINIMIZED, NULL);
             evas_object_smart_callback_call(obj, "iconified", NULL);
             if (_elm_config->atspi_mode)
               efl_access_window_minimized_signal_emit(obj);
          }
        else
          {
             efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_NORMAL, NULL);
             if (_elm_config->atspi_mode)
               efl_access_window_restored_signal_emit(obj);
          }
     }
   if (ch_sticky)
     {
        if (sd->sticky)
          efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_STICK, NULL);
        else
          efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_UNSTICK, NULL);
     }
#ifdef HAVE_ELEMENTARY_WL2
   if (sd->wl.win)
     {
        if (sd->csd.cur_focus != ecore_wl2_window_activated_get(sd->wl.win))
          _elm_win_frame_style_update(sd, 0, 1);
     }
#endif
   if (ch_fullscreen)
     {
        Eina_Bool fullscreen;
        ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
        _elm_win_frame_style_update(sd, 0, 1);
        fullscreen = sd->fullscreen;
        if (sd->fullscreen)
          {
             evas_object_smart_callback_call(obj, "fullscreen", NULL);
          }
        else
          {
             evas_object_smart_callback_call(obj, "unfullscreen", NULL);
          }
        efl_event_callback_call(obj, EFL_UI_WIN_EVENT_FULLSCREEN_CHANGED, &fullscreen);
     }
   if (ch_maximized)
     {
        Eina_Bool maximized;
        ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
        _elm_win_frame_style_update(sd, 0, 1);
        maximized = sd->maximized;
        if (sd->maximized)
          {
             evas_object_smart_callback_call(obj, "maximized", NULL);
             if (_elm_config->atspi_mode)
               efl_access_window_maximized_signal_emit(obj);
          }
        else
          {
             evas_object_smart_callback_call(obj, "unmaximized", NULL);
             if (_elm_config->atspi_mode)
               efl_access_window_restored_signal_emit(obj);
          }
        efl_event_callback_call(obj, EFL_UI_WIN_EVENT_MAXIMIZED_CHANGED, &maximized);
     }
   if (ch_profile)
     {
        ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
        _elm_win_profile_update(sd);
     }
   if (ch_wm_rotation)
     {
        efl_ui_win_rotation_set(obj, ecore_evas_rotation_get(sd->ee));

        efl_event_callback_legacy_call
          (obj, EFL_UI_WIN_EVENT_WM_ROTATION_CHANGED, NULL);
     }
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_ui_focus_object_on_focus_update(Eo *obj, Efl_Ui_Win_Data *sd)
{
   if (!efl_ui_focus_object_on_focus_update(efl_super(obj, MY_CLASS)))
     return EINA_TRUE;

   if (sd->img_obj)
     evas_object_focus_set(sd->img_obj, efl_ui_focus_object_focus_get(obj));
   else
     evas_object_focus_set(obj, efl_ui_focus_object_focus_get(obj));

   return EINA_TRUE;
}

static Eina_Bool
_key_action_return(Evas_Object *obj EINA_UNUSED, const char *params EINA_UNUSED)
{
   return EINA_FALSE;
}

static Eina_Bool
_key_action_move(Evas_Object *obj, const char *params)
{
   const char *dir = params;

   _elm_widget_focus_auto_show(obj);

   Efl_Ui_Focus_Direction focus_dir;

   if (!strcmp(dir, "previous"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_PREVIOUS;
   else if (!strcmp(dir, "next"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_NEXT;
   else if (!strcmp(dir, "left"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_LEFT;
   else if (!strcmp(dir, "right"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_RIGHT;
   else if (!strcmp(dir, "up"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_UP;
   else if (!strcmp(dir, "down"))
     focus_dir = EFL_UI_FOCUS_DIRECTION_DOWN;
   else return EINA_FALSE;


  // The handling for legacy is different due to elm_object_next set
  if (elm_widget_is_legacy(obj))
    elm_object_focus_next(obj, (Elm_Focus_Direction)focus_dir);
  else
    {
       Efl_Ui_Widget *o;

       o = efl_ui_focus_manager_move(obj, focus_dir);
       if (!o)
         {
            if (focus_dir == EFL_UI_FOCUS_DIRECTION_NEXT || focus_dir == EFL_UI_FOCUS_DIRECTION_PREVIOUS)
              {
                 Efl_Ui_Focus_Object *root;

                 root = efl_ui_focus_manager_root_get(obj);
                 efl_ui_focus_manager_setup_on_first_touch(obj, focus_dir, root);
              }
         }
    }

   return EINA_TRUE;
}

/* forward events sent to evas to the window */
static void
_evas_event_key_cb(void *data, const Efl_Event *ev)
{
   Eo *win = data;
   Eo *evt = ev->info;
   Efl_Input_Key_Data *evdata;

   evdata = efl_data_scope_get(evt, EFL_INPUT_KEY_CLASS);
   if (!evdata || evdata->win_fed)
     return;

   // evas_callbacks will send the event to the focused object (ie. this win)
   if (evas_focus_get(evas_object_evas_get(win)) == win)
     return;

   efl_event_callback_call(win, ev->desc, evt);
}

static void
_evas_event_pointer_cb(void *data, const Efl_Event *ev)
{
   Eo *win = data;
   Eo *evt = ev->info;
   Efl_Input_Pointer_Data *evdata;

   evdata = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!evdata || evdata->win_fed)
     return;

   efl_event_callback_call(win, ev->desc, evt);
}

/* feed events from the window to evas - for fake inputs */
static void
_evas_event_key_feed_fake_cb(void *data, const Efl_Event *ev)
{
   Eo *evas = data;
   Efl_Input_Event *evt = ev->info;
   Efl_Input_Key_Data *evdata;

   if (!efl_input_fake_get(evt))
     return;

   evdata = efl_data_scope_get(evt, EFL_INPUT_KEY_CLASS);
   if (!evdata || evdata->win_fed)
     return;
   evdata->win_fed = EINA_TRUE;

   efl_event_callback_call(evas, ev->desc, evt);
   evdata->win_fed = EINA_FALSE;
   evdata->evas_done = EINA_FALSE;
}

static void
_evas_event_pointer_feed_fake_cb(void *data, const Efl_Event *ev)
{
   Eo *evas = data;
   Efl_Input_Event *evt = ev->info;
   Efl_Input_Pointer_Data *evdata;

   if (!efl_input_fake_get(evt))
     return;

   evdata = efl_data_scope_get(evt, EFL_INPUT_POINTER_CLASS);
   if (!evdata || evdata->win_fed)
     return;
   evdata->win_fed = EINA_TRUE;

   efl_event_callback_call(evas, ev->desc, evt);
   evdata->win_fed = EINA_FALSE;
   evdata->evas_done = EINA_FALSE;
}

EFL_CALLBACKS_ARRAY_DEFINE(_elm_win_evas_feed_fake_callbacks,
{ EFL_EVENT_POINTER_MOVE, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_DOWN, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_UP, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_IN, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_OUT, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_CANCEL, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_POINTER_WHEEL, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_FINGER_MOVE, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_FINGER_DOWN, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_FINGER_UP, _evas_event_pointer_feed_fake_cb },
{ EFL_EVENT_KEY_DOWN, _evas_event_key_feed_fake_cb },
{ EFL_EVENT_KEY_UP, _evas_event_key_feed_fake_cb })

static void
_elm_win_evas_render_post(void *data,
                          Evas *e EINA_UNUSED,
                          void *event_info)
{
   Efl_Gfx_Event_Render_Post *ev = event_info;
   Eo *win = data;

   efl_event_callback_call(win, EFL_CANVAS_SCENE_EVENT_RENDER_POST, ev);
}

static void
_elm_win_evas_render_pre(void *data,
                          Evas *e EINA_UNUSED,
                          void *event_info EINA_UNUSED)
{
   Eo *win = data;

   _elm_win_throttle_ok = EINA_TRUE;
   efl_event_callback_call(win, EFL_CANVAS_SCENE_EVENT_RENDER_PRE, NULL);
}

static void
_elm_win_evas_focus_in(void *data,
                       Evas *e EINA_UNUSED,
                       void *event_info EINA_UNUSED)
{
   Eo *win = data;

   _elm_win_throttle_ok = EINA_TRUE;
   efl_event_callback_call(win, EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN, NULL);
}

static void
_elm_win_evas_focus_out(void *data,
                        Evas *e EINA_UNUSED,
                        void *event_info EINA_UNUSED)
{
   Eo *win = data;

   efl_event_callback_call(win, EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_OUT, NULL);
}

static void
_evas_event_focus_object_cb(void *data, const Efl_Event *ev)
{
   Eo *win = data;

   if (ev->desc == EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_IN)
     _elm_win_throttle_ok = EINA_TRUE;

   efl_event_callback_call(win, ev->desc, ev->info);
}

static void
_elm_win_evas_device_changed(void *data,
                             Evas *e EINA_UNUSED,
                             void *event_info)
{
   Eo *device = event_info;
   Eo *win = data;

   efl_event_callback_call(win, EFL_CANVAS_SCENE_EVENT_DEVICE_CHANGED, device);
}

static void
_win_event_add_cb(void *data, const Efl_Event *ev)
{
   const Efl_Callback_Array_Item_Full *array = ev->info;
   Efl_Ui_Win_Data *sd = data;
   Efl_Ui_Win *win = ev->object;
   int i;

   for (i = 0; array[i].desc; i++)
     {
        if (array[i].desc == EFL_EVENT_POINTER_MOVE)
          {
             if (!(sd->event_forward.pointer_move++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_DOWN)
          {
             if (!(sd->event_forward.pointer_down++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_UP)
          {
             if (!(sd->event_forward.pointer_up++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_IN)
          {
             if (!(sd->event_forward.pointer_in++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_OUT)
          {
             if (!(sd->event_forward.pointer_out++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_CANCEL)
          {
             if (!(sd->event_forward.pointer_cancel++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_WHEEL)
          {
             if (!(sd->event_forward.pointer_wheel++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_MOVE)
          {
             if (!(sd->event_forward.finger_move++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_DOWN)
          {
             if (!(sd->event_forward.finger_down++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_UP)
          {
             if (!(sd->event_forward.finger_up++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_KEY_DOWN)
          {
             // Legacy API: Must grab key
             if (elm_widget_is_legacy(win)) return;
             if (!(sd->event_forward.key_down++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_key_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_KEY_UP)
          {
             // Legacy API: Must grab key
             if (elm_widget_is_legacy(win)) return;
             if (!(sd->event_forward.key_up++))
               efl_event_callback_add(sd->evas, array[i].desc,
                                     _evas_event_key_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_RENDER_POST)
          {
             if (!(sd->event_forward.render_post++))
               evas_event_callback_add(sd->evas, EVAS_CALLBACK_RENDER_POST,
                                       _elm_win_evas_render_post, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_RENDER_PRE)
          {
             if (!(sd->event_forward.render_pre++))
               evas_event_callback_add(sd->evas, EVAS_CALLBACK_RENDER_PRE,
                                       _elm_win_evas_render_pre, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN)
          {
             if (!(sd->event_forward.focus_in++))
               evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_FOCUS_IN,
                                       _elm_win_evas_focus_in, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_OUT)
          {
             if (!(sd->event_forward.focus_out++))
               evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_FOCUS_OUT,
                                       _elm_win_evas_focus_out, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_IN)
          {
             if (!(sd->event_forward.object_focus_in++))
               efl_event_callback_add(sd->evas, EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_IN,
                                       _evas_event_focus_object_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_OUT)
          {
             if (!(sd->event_forward.object_focus_out++))
               efl_event_callback_add(sd->evas, EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_OUT,
                                       _evas_event_focus_object_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_DEVICE_CHANGED)
          {
             if (!(sd->event_forward.device_changed++))
               evas_event_callback_add(sd->evas, EVAS_CALLBACK_DEVICE_CHANGED,
                                       _elm_win_evas_device_changed, win);
          }
     }
}

static void
_win_event_del_cb(void *data, const Efl_Event *ev)
{
   const Efl_Callback_Array_Item_Full *array = ev->info;
   Efl_Ui_Win_Data *sd = data;
   Efl_Ui_Win *win = ev->object;
   int i;

   for (i = 0; array[i].desc; i++)
     {
        if (array[i].desc == EFL_EVENT_POINTER_MOVE)
          {
             if (!(--sd->event_forward.pointer_move))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_DOWN)
          {
             if (!(--sd->event_forward.pointer_down))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_UP)
          {
             if (!(--sd->event_forward.pointer_up))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_IN)
          {
             if (!(--sd->event_forward.pointer_in))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_OUT)
          {
             if (!(--sd->event_forward.pointer_out))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_CANCEL)
          {
             if (!(--sd->event_forward.pointer_cancel))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_POINTER_WHEEL)
          {
             if (!(--sd->event_forward.pointer_wheel))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_MOVE)
          {
             if (!(--sd->event_forward.finger_move))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_DOWN)
          {
             if (!(--sd->event_forward.finger_down))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_FINGER_UP)
          {
             if (!(--sd->event_forward.finger_up))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_pointer_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_KEY_DOWN)
          {
             // Legacy API: Must grab key
             if (elm_widget_is_legacy(win)) return;
             if (!(--sd->event_forward.key_down))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_key_cb, win);
          }
        else if (array[i].desc == EFL_EVENT_KEY_UP)
          {
             // Legacy API: Must grab key
             if (elm_widget_is_legacy(win)) return;
             if (!(--sd->event_forward.key_up))
               efl_event_callback_del(sd->evas, array[i].desc,
                                     _evas_event_key_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_RENDER_POST)
          {
             if (!(--sd->event_forward.render_post))
               evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_RENDER_POST,
                                            _elm_win_evas_render_post, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_RENDER_PRE)
          {
             if (!(--sd->event_forward.render_pre))
               evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_RENDER_PRE,
                                            _elm_win_evas_render_pre, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_IN)
          {
             if (!(--sd->event_forward.focus_in))
               evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_FOCUS_IN,
                                            _elm_win_evas_focus_in, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_SCENE_FOCUS_OUT)
          {
             if (!(--sd->event_forward.focus_out))
               evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_FOCUS_OUT,
                                            _elm_win_evas_focus_out, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_IN)
          {
             if (!(--sd->event_forward.object_focus_in))
               efl_event_callback_del(sd->evas, EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_IN,
                                            _evas_event_focus_object_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_OUT)
          {
             if (!(--sd->event_forward.object_focus_out))
               efl_event_callback_del(sd->evas, EFL_CANVAS_SCENE_EVENT_OBJECT_FOCUS_OUT,
                                            _evas_event_focus_object_cb, win);
          }
        else if (array[i].desc == EFL_CANVAS_SCENE_EVENT_DEVICE_CHANGED)
          {
             if (!(--sd->event_forward.device_changed))
               evas_event_callback_del_full(sd->evas, EVAS_CALLBACK_DEVICE_CHANGED,
                                            _elm_win_evas_device_changed, win);
          }
     }
}

static void
_win_paused(void *data, const Efl_Event *ev)
{
   Efl_Ui_Win_Data *sd = data;

   if (sd->paused)
     {
        ERR("A window did receive a pause event while still paused. Dismissing.");
        return ;
     }
   sd->paused = EINA_TRUE;
   _paused_windows++;

   if (_elm_win_count == _paused_windows)
     efl_event_callback_call(efl_loop_get(ev->object), EFL_APP_EVENT_PAUSE, NULL);
}

EFL_CALLBACKS_ARRAY_DEFINE(_elm_win_tracking,
                           { EFL_EVENT_CALLBACK_ADD, _win_event_add_cb },
                           { EFL_EVENT_CALLBACK_DEL, _win_event_del_cb },
                           { EFL_UI_WIN_EVENT_PAUSE, _win_paused })

static void
_elm_win_cb_mouse_up(void *data, const Efl_Event *ev EINA_UNUSED)
{
   DBG("Evas mouse up event");
   /*Currently wayland server didn't send mouse up event after resize the window*/
   Efl_Ui_Win_Data *sd = data;
   if(sd->resizing) sd->resizing = EINA_FALSE;
}

static void
_elm_win_resume(void *data, const Efl_Event *ev)
{
   Efl_Ui_Win_Data *sd = data;

   if (!sd->paused) return ;

   efl_event_callback_call(sd->obj, EFL_UI_WIN_EVENT_RESUME, NULL);
   sd->paused = EINA_FALSE;

   if (_elm_win_count == _paused_windows)
     efl_event_callback_call(efl_loop_get(ev->object), EFL_APP_EVENT_RESUME, NULL);

   _paused_windows--;
}

EFL_CALLBACKS_ARRAY_DEFINE(_elm_evas_tracking,
                           { EFL_EVENT_POINTER_UP, _elm_win_cb_mouse_up },
                           { EFL_CANVAS_SCENE_EVENT_RENDER_PRE, _elm_win_resume })

static void
_deferred_ecore_evas_free(void *data)
{
   ecore_evas_free(data);
   _elm_win_deferred_free--;
}

static inline Edje_Object *
_elm_win_modal_blocker_edje_get(Efl_Ui_Win_Data *sd)
{
   /* Legacy theme compatibility */
   const char *version = edje_object_data_get(sd->legacy.edje, "version");
   int v = version ? atoi(version) : 0;
   if (v < FRAME_OBJ_THEME_MIN_VERSION)
     {
        DBG("Detected legacy theme (<1.19) for modal window blocker.");
        return sd->legacy.edje;
     }
   return sd->frame_obj;
}

static void
_elm_win_modality_increment(Efl_Ui_Win_Data *modalsd)
{
   Efl_Ui_Win *current;
   Eina_List *l;
   Eina_Bool is_legacy = elm_widget_is_legacy(modalsd->obj);

   EINA_LIST_FOREACH(_elm_win_list, l, current)
     {
        ELM_WIN_DATA_GET_OR_RETURN(current, cursd);
        if (modalsd != cursd)
          cursd->modal_count++;
        if (cursd->modal_count > 0)
          {
             Edje_Object *ed = _elm_win_modal_blocker_edje_get(cursd);
             if (is_legacy)
               edje_object_signal_emit(ed, "elm,action,show_blocker", "elm");
             else
               edje_object_signal_emit(ed, "efl,action,show_blocker", "efl");
             efl_event_callback_legacy_call
                   (cursd->main_menu, EFL_UI_WIN_EVENT_ELM_ACTION_BLOCK_MENU, NULL);
             _elm_win_frame_style_update(cursd, 0, 1);
          }
     }
}

static void
_elm_win_modality_decrement(Efl_Ui_Win_Data *modalsd)
{
   Efl_Ui_Win *current;
   Eina_List *l;
   Eina_Bool is_legacy = elm_widget_is_legacy(modalsd->obj);

   EINA_LIST_FOREACH(_elm_win_list, l, current)
     {
        ELM_WIN_DATA_GET_OR_RETURN(current, cursd);
        if ((modalsd != cursd) && (cursd->modal_count > 0))
          cursd->modal_count--;
        if (cursd->modal_count == 0)
          {
             Edje_Object *ed = _elm_win_modal_blocker_edje_get(cursd);
             if (is_legacy)
               edje_object_signal_emit(ed, "elm,action,hide_blocker", "elm");
             else
               edje_object_signal_emit(ed, "efl,action,hide_blocker", "efl");
             efl_event_callback_legacy_call
                   (cursd->main_menu, ELM_MENU_EVENT_ELM_ACTION_UNBLOCK_MENU, NULL);
             _elm_win_frame_style_update(cursd, 0, 1);
          }
     }
}

static void
_efl_ui_win_show(Eo *obj, Efl_Ui_Win_Data *sd)
{
   Eina_Bool do_eval = EINA_FALSE;

   sd->shown = EINA_TRUE;
   if (sd->modal_count)
     {
        /* FIXME FIXME FIXME
         * Ugly code flow: legacy code had an early return in smart_show, ie.
         * evas object show would be processed but smart object show would be
         * aborted. This super call tries to simulate that. */
        efl_gfx_entity_visible_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), EINA_TRUE);
        return;
     }

   if ((sd->modal) && (!evas_object_visible_get(obj)))
     _elm_win_modality_increment(sd);

   if (!evas_object_visible_get(obj)) do_eval = EINA_TRUE;
   efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), EINA_TRUE);

   if (sd->deferred_resize_job)
     _elm_win_resize_job(sd->obj);
   evas_smart_objects_calculate(evas_object_evas_get(obj));

   TRAP(sd, show);

   if (_elm_config->atspi_mode)
     {
        Eo *root;
        efl_access_window_created_signal_emit(obj);
        root = efl_access_object_access_root_get();
        if (root)
           efl_access_children_changed_added_signal_emit(root, obj);
     }

   if (do_eval)
     {
        if (_elm_win_state_eval_timer)
          {
             ecore_timer_del(_elm_win_state_eval_timer);
             _elm_win_state_eval_timer = NULL;
          }
        _elm_win_state_eval(NULL);
     }

   if (sd->shot.info) _shot_handle(sd);

   if (!sd->first_draw) return;
   if (sd->frame_obj)
     {
        evas_object_show(sd->frame_obj);
     }
   if (sd->img_obj)
     {
        evas_object_show(sd->img_obj);
     }
   if (sd->pointer.obj)
     {
        evas_object_show(sd->pointer.obj);
     }
}

static void
_efl_ui_win_hide(Eo *obj, Efl_Ui_Win_Data *sd)
{
   if (sd->modal_count)
     {
        /* FIXME FIXME FIXME
         * Ugly code flow: legacy code had an early return in smart_show, ie.
         * evas object show would be processed but smart object show would be
         * aborted. This super call tries to simulate that. */
        efl_gfx_entity_visible_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), EINA_FALSE);
        return;
     }

   _elm_win_state_eval_queue(EINA_FALSE);

   if ((sd->modal) && (evas_object_visible_get(obj)))
     _elm_win_modality_decrement(sd);

   efl_gfx_entity_visible_set(efl_super(obj, MY_CLASS), EINA_FALSE);
   TRAP(sd, hide);

   if (sd->frame_obj)
     {
        evas_object_hide(sd->frame_obj);
     }
   if (sd->img_obj)
     {
        evas_object_hide(sd->img_obj);
     }
   if (sd->pointer.obj)
     {
        evas_object_hide(sd->pointer.obj);
     }

   if (_elm_config->atspi_mode)
     {
        Eo *root;
        root = efl_access_object_access_root_get();
        efl_access_window_destroyed_signal_emit(obj);
        if (root)
           efl_access_children_changed_del_signal_emit(root, obj);
     }

   if (_elm_win_policy_quit_triggered(obj))
     _elm_win_flush_cache_and_exit(obj);
}

EOLIAN static void
_efl_ui_win_efl_gfx_entity_visible_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool vis)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_VISIBLE, 0, vis))
     return;

   if (vis) _efl_ui_win_show(obj, sd);
   else _efl_ui_win_hide(obj, sd);
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_canvas_scene_pointer_position_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eo *dev, Eina_Position2D *pos)
{
   return efl_canvas_scene_pointer_position_get(sd->evas, dev, pos);
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_canvas_pointer_pointer_inside_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eo *dev)
{
   return efl_canvas_pointer_inside_get(sd->evas, dev);
}

/* multi touch support */
static Eina_Bool
_input_pointer_iterator_next(Input_Pointer_Iterator *it, void **data)
{
   Eo *sub;

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

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

static Eo *
_input_pointer_iterator_get_container(Input_Pointer_Iterator *it)
{
   return (Eo *) it->object;
}

static void
_input_pointer_iterator_free(Input_Pointer_Iterator *it)
{
   Efl_Input_Pointer *ptr;

   EINA_LIST_FREE(it->list, ptr)
     efl_unref(ptr);
   eina_iterator_free(it->real_iterator);
   free(it);
}

EOLIAN static Eina_Iterator *
_efl_ui_win_pointer_iterate(const Eo *obj, Efl_Ui_Win_Data *sd,
                                       Eina_Bool hover EINA_UNUSED)
{
   Input_Pointer_Iterator *it;
   Eina_List *list = NULL;
   int i, cnt;

   // Note: "hover" is here as a possible extension to this API. At the moment
   // I don't have any device that could track the position of hovering fingers
   // and Evas also wouldn't track those.

   ELM_WIDGET_DATA_GET_OR_RETURN(obj, wd, NULL);

   cnt = evas_touch_point_list_count(sd->evas);
   if (!cnt) return NULL;

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

   EINA_MAGIC_SET(&it->iterator, EINA_MAGIC_ITERATOR);

   for (i = 0; i < cnt; i++)
     {
        Efl_Input_Pointer_Data *ptrdata;
        Evas_Touch_Point_State state;
        Efl_Input_Pointer *ptr;
        double x, y;

        ptr = efl_input_pointer_instance_get( (Eo *) obj, (void **) &ptrdata);
        if (!ptrdata) break;

        ptrdata->touch_id = evas_touch_point_list_nth_id_get(sd->evas, i);
        _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_TOUCH_ID);

        // Note that "still" maps to "down" here.
        state = evas_touch_point_list_nth_state_get(sd->evas, i);
        switch (state)
          {
           case EVAS_TOUCH_POINT_DOWN:   ptrdata->action = EFL_POINTER_ACTION_DOWN; break;
           case EVAS_TOUCH_POINT_UP:     ptrdata->action = EFL_POINTER_ACTION_UP; break;
           case EVAS_TOUCH_POINT_MOVE:   ptrdata->action = EFL_POINTER_ACTION_MOVE; break;
           case EVAS_TOUCH_POINT_STILL:  ptrdata->action = EFL_POINTER_ACTION_DOWN; break;
           case EVAS_TOUCH_POINT_CANCEL: ptrdata->action = EFL_POINTER_ACTION_CANCEL; break;
           default:                      ptrdata->action = EFL_POINTER_ACTION_NONE; break;
          }

        evas_canvas_touch_point_list_nth_xy_get(sd->evas, i, &x, &y);
        _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_X);
        _efl_input_value_mark(ptrdata, EFL_INPUT_VALUE_Y);
        ptrdata->cur.x = x;
        ptrdata->cur.y = y;
        ptrdata->prev = ptrdata->cur;

        list = eina_list_append(list, ptr);
     }

   it->list = list;
   it->real_iterator = eina_list_iterator_new(it->list);
   it->iterator.version = EINA_ITERATOR_VERSION;
   it->iterator.next = FUNC_ITERATOR_NEXT(_input_pointer_iterator_next);
   it->iterator.get_container = FUNC_ITERATOR_GET_CONTAINER(_input_pointer_iterator_get_container);
   it->iterator.free = FUNC_ITERATOR_FREE(_input_pointer_iterator_free);
   it->object = obj;

   return &it->iterator;
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_canvas_scene_image_max_size_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D *max)
{
   return efl_canvas_scene_image_max_size_get(sd->evas, max);
}

EOLIAN static void
_efl_ui_win_efl_canvas_scene_group_objects_calculate(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   evas_smart_objects_calculate(sd->evas);
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_canvas_scene_group_objects_calculating_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return efl_canvas_scene_group_objects_calculating_get(sd->evas);
}

EOLIAN static Eina_Iterator *
_efl_ui_win_efl_canvas_scene_objects_at_xy_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *objs = NULL;
   objs = evas_objects_at_xy_get(sd->evas, pos.x, pos.y, include_pass_events_objects, include_hidden_objects);
   return eina_list_iterator_new(objs); // FIXME: This leaks the list!
}

EOLIAN static Efl_Gfx_Entity *
_efl_ui_win_efl_canvas_scene_object_top_at_xy_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Position2D pos, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   return evas_object_top_at_xy_get(sd->evas, pos.x, pos.y, include_pass_events_objects, include_hidden_objects);
}

EOLIAN static Eina_Iterator *
_efl_ui_win_efl_canvas_scene_objects_in_rectangle_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Rect r, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   Eina_List *objs = NULL;
   objs = evas_objects_in_rectangle_get(sd->evas, r.x, r.y, r.w, r.h, include_pass_events_objects, include_hidden_objects);
   return eina_list_iterator_new(objs); // FIXME: This leaks the list!
}

EOLIAN static Efl_Gfx_Entity *
_efl_ui_win_efl_canvas_scene_object_top_in_rectangle_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Rect r, Eina_Bool include_pass_events_objects, Eina_Bool include_hidden_objects)
{
   return evas_object_top_in_rectangle_get(sd->evas, r.x, r.y, r.w, r.h, include_pass_events_objects, include_hidden_objects);
}

EOLIAN static Efl_Input_Device *
_efl_ui_win_efl_canvas_scene_device_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *name)
{
   return efl_canvas_scene_device_get(sd->evas, name);
}

EOLIAN static Efl_Input_Device *
_efl_ui_win_efl_canvas_scene_seat_default_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return efl_canvas_scene_seat_default_get(sd->evas);
}

EOLIAN static Efl_Input_Device *
_efl_ui_win_efl_canvas_scene_seat_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, int id)
{
   return efl_canvas_scene_seat_get(sd->evas, id);
}

static void
_elm_win_on_parent_del(void *data,
                       Evas *e EINA_UNUSED,
                       Evas_Object *obj,
                       void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   if (obj == sd->parent) sd->parent = NULL;
}

static void
_elm_win_focus_target_move(void *data,
                           Evas *e EINA_UNUSED,
                           Evas_Object *obj EINA_UNUSED,
                           void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   sd->focus_highlight.geometry_changed = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

static void
_elm_win_focus_target_resize(void *data,
                             Evas *e EINA_UNUSED,
                             Evas_Object *obj EINA_UNUSED,
                             void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   sd->focus_highlight.geometry_changed = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

static void
_elm_win_focus_target_del(void *data,
                          Evas *e EINA_UNUSED,
                          Evas_Object *obj EINA_UNUSED,
                          void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   sd->focus_highlight.cur.target = NULL;

   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

static Evas_Object *
_elm_win_focus_target_get(Evas_Object *obj)
{
   Evas_Object *o = obj;

   while (o)
     {
        if (elm_widget_is(o))
          {
             if (!elm_widget_highlight_ignore_get(o))
               break;
          }
        o = elm_widget_parent_widget_get(o);
     }

   return o;
}

static void
_elm_win_focus_target_callbacks_add(Efl_Ui_Win_Data *sd)
{
   Evas_Object *obj = sd->focus_highlight.cur.target;
   if (!obj) return;

   evas_object_event_callback_add
     (obj, EVAS_CALLBACK_MOVE, _elm_win_focus_target_move, sd->obj);
   evas_object_event_callback_add
     (obj, EVAS_CALLBACK_RESIZE, _elm_win_focus_target_resize, sd->obj);
}

static void
_elm_win_focus_target_callbacks_del(Efl_Ui_Win_Data *sd)
{
   Evas_Object *obj = sd->focus_highlight.cur.target;

   evas_object_event_callback_del_full
     (obj, EVAS_CALLBACK_MOVE, _elm_win_focus_target_move, sd->obj);
   evas_object_event_callback_del_full
     (obj, EVAS_CALLBACK_RESIZE, _elm_win_focus_target_resize, sd->obj);
}

static void
_elm_win_object_focus_in(void *data,
                         Evas *e EINA_UNUSED,
                         void *event_info)
{
   Evas_Object *obj = event_info, *target;
   ELM_WIN_DATA_GET(data, sd);

   if (sd->focus_highlight.cur.target == obj)
     return;

   target = _elm_win_focus_target_get(obj);
   sd->focus_highlight.cur.target = target;

   if (target)
     {
        if (elm_widget_highlight_in_theme_get(target))
          sd->focus_highlight.cur.in_theme = EINA_TRUE;
        else
          _elm_win_focus_target_callbacks_add(sd);
        evas_object_event_callback_add
              (target, EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj);
     }

   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

static void
_elm_win_object_focus_out(void *data,
                          Evas *e EINA_UNUSED,
                          void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   if (!sd->focus_highlight.cur.target)
     return;

   if (!sd->focus_highlight.cur.in_theme)
     _elm_win_focus_target_callbacks_del(sd);

   evas_object_event_callback_del_full
      (sd->focus_highlight.cur.target,
       EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj);

   sd->focus_highlight.cur.target = NULL;
   sd->focus_highlight.cur.in_theme = EINA_FALSE;

   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

static void
_elm_win_focus_highlight_shutdown(Efl_Ui_Win_Data *sd)
{
   _elm_win_focus_highlight_reconfigure_job_stop(sd);
   if (sd->focus_highlight.cur.target)
     {
        if (elm_widget_is_legacy(sd->obj))
          elm_widget_signal_emit(sd->focus_highlight.cur.target,
                                 "elm,action,focus_highlight,hide", "elm");
        else
          elm_widget_signal_emit(sd->focus_highlight.cur.target,
                                 "efl,action,focus_highlight,hide", "efl");
        _elm_win_focus_target_callbacks_del(sd);
        evas_object_event_callback_del_full
           (sd->focus_highlight.cur.target,
            EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj);
        sd->focus_highlight.cur.target = NULL;
     }
   ELM_SAFE_FREE(sd->focus_highlight.fobj, evas_object_del);

   evas_event_callback_del_full
     (sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN,
     _elm_win_object_focus_in, sd->obj);
   evas_event_callback_del_full
     (sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT,
     _elm_win_object_focus_out, sd->obj);
}

static void
_win_img_hide(void *data,
              Evas *e EINA_UNUSED,
              Evas_Object *obj EINA_UNUSED,
              void *event_info EINA_UNUSED)
{
   Efl_Ui_Win *real_win = elm_widget_top_get(data);
   efl_ui_focus_manager_redirect_set(real_win, NULL);
}

static void
_win_img_mouse_up(void *data,
                  Evas *e EINA_UNUSED,
                  Evas_Object *obj EINA_UNUSED,
                  void *event_info)
{
   Evas_Event_Mouse_Up *ev = event_info;
   if (!(ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD))
     elm_widget_focus_mouse_up_handle(evas_object_widget_parent_find(data));
}

static void
_win_img_focus_in(void *data,
                  Evas *e EINA_UNUSED,
                  Evas_Object *obj EINA_UNUSED,
                  void *event_info EINA_UNUSED)
{
   Efl_Ui_Win *real_win = elm_widget_top_get(data);
   efl_ui_focus_manager_redirect_set(real_win, data);
   efl_ui_focus_manager_focus_set(data, efl_ui_focus_manager_root_get(data));
}

static void
_win_img_focus_out(void *data,
                   Evas *e EINA_UNUSED,
                   Evas_Object *obj EINA_UNUSED,
                   void *event_info EINA_UNUSED)
{
   Efl_Ui_Win *real_win = elm_widget_top_get(data);
   efl_ui_focus_manager_redirect_set(real_win, NULL);
}

static void
_elm_win_on_img_obj_del(void *data,
                        Evas *e EINA_UNUSED,
                        Evas_Object *obj EINA_UNUSED,
                        void *event_info EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);
   _elm_win_img_callbacks_del(sd->obj, sd->img_obj);
   sd->img_obj = NULL;
}

static void
_elm_win_img_callbacks_del(Evas_Object *obj, Evas_Object *imgobj)
{
   if (!imgobj) return;
   evas_object_event_callback_del_full
     (imgobj, EVAS_CALLBACK_DEL, _elm_win_on_img_obj_del, obj);
   evas_object_event_callback_del_full
     (imgobj, EVAS_CALLBACK_HIDE, _win_img_hide, obj);
   evas_object_event_callback_del_full
     (imgobj, EVAS_CALLBACK_MOUSE_UP, _win_img_mouse_up, obj);
   evas_object_event_callback_del_full
     (imgobj, EVAS_CALLBACK_FOCUS_IN, _win_img_focus_in, obj);
   evas_object_event_callback_del_full
     (imgobj, EVAS_CALLBACK_FOCUS_OUT, _win_img_focus_out, obj);
}

EOLIAN static void
_efl_ui_win_efl_canvas_group_group_del(Eo *obj, Efl_Ui_Win_Data *sd)
{
   efl_event_freeze(sd->evas);

   if ((sd->modal) && (evas_object_visible_get(obj)))
     _elm_win_modality_decrement(sd);

   if ((sd->modal) && (sd->modal_count > 0))
     ERR("Deleted modal win was blocked by another modal win which was created after creation of that win.");

   evas_object_event_callback_del_full(sd->legacy.edje,
                                       EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                       _elm_win_on_resize_obj_changed_size_hints,
                                       obj);

   efl_event_callback_array_del(sd->evas, _elm_evas_tracking(), sd);
   efl_event_callback_array_del(obj, _elm_win_evas_feed_fake_callbacks(), sd->evas);
   efl_event_callback_array_del(obj, _elm_win_tracking(), sd);
   evas_object_del(sd->legacy.box);
   evas_object_del(sd->legacy.edje);

   /* NB: child deletion handled by parent's smart del */

   if ((sd->type != EFL_UI_WIN_TYPE_FAKE) && (trap) && (trap->del))
     trap->del(sd->trap_data, obj);

   if (sd->parent)
     {
        evas_object_event_callback_del_full
          (sd->parent, EVAS_CALLBACK_DEL, _elm_win_on_parent_del, obj);
        sd->parent = NULL;
     }

   if (sd->autodel_clear) *(sd->autodel_clear) = -1;

   if (_elm_config->atspi_mode)
     efl_access_window_destroyed_signal_emit(obj);

   _elm_win_list = eina_list_remove(_elm_win_list, obj);
   _elm_win_count--;
   _elm_win_state_eval_queue(EINA_FALSE);

   if (_elm_win_count == _paused_windows)
     efl_event_callback_call(efl_loop_get(obj), EFL_APP_EVENT_PAUSE, NULL);

   if (sd->ee)
     {
        ecore_evas_callback_delete_request_set(sd->ee, NULL);
        ecore_evas_callback_resize_set(sd->ee, NULL);
     }

   eina_stringshare_del(sd->shot.info);
   ecore_timer_del(sd->shot.timer);

#ifdef HAVE_ELEMENTARY_X
   ecore_event_handler_del(sd->x.client_message_handler);
   ecore_event_handler_del(sd->x.property_handler);
#endif
#ifdef HAVE_ELEMENTARY_WL2
   ecore_event_handler_del(sd->wl.configure_handler);
   if (sd->pointer.obj) evas_object_del(sd->pointer.obj);
   if (sd->pointer.ee) ecore_evas_free(sd->pointer.ee);
   sd->pointer.surf = NULL;
#endif
#ifdef HAVE_ELEMENTARY_WIN32
   ecore_event_handler_del(sd->win32.key_down_handler);
#endif

   if (sd->type == ELM_WIN_INLINED_IMAGE)
     {
        _elm_win_img_callbacks_del(obj, sd->img_obj);
        sd->img_obj = NULL;
     }
   else
     {
        if (sd->ee && (sd->type != EFL_UI_WIN_TYPE_FAKE))
          {
             ecore_evas_manual_render_set(sd->ee, EINA_TRUE);
             edje_object_freeze(sd->frame_obj);
             ecore_job_add(_deferred_ecore_evas_free, sd->ee);
             _elm_win_deferred_free++;
          }
     }

   _elm_win_focus_highlight_shutdown(sd);
   eina_stringshare_del(sd->focus_highlight.style);

   eina_stringshare_del(sd->title);
   eina_stringshare_del(sd->icon_name);
   eina_stringshare_del(sd->role);
   eina_stringshare_del(sd->name);
   eina_stringshare_del(sd->accel_pref);
   eina_stringshare_del(sd->stack_id);
   eina_stringshare_del(sd->stack_master_id);
   evas_object_del(sd->icon);
   evas_object_del(sd->main_menu);
   evas_object_del(sd->indicator);

   sd->focus_highlight.style = NULL;
   sd->title = NULL;
   sd->icon_name = NULL;
   sd->role = NULL;
   sd->name = NULL;
   sd->icon = NULL;
   sd->main_menu = NULL;

   _elm_win_profile_del(sd);
   _elm_win_available_profiles_del(sd);
   eina_array_free(sd->profile.available);
   sd->profile.available = NULL;

   eina_array_free(sd->planned_changes);
   sd->planned_changes = NULL;

   free(sd->wm_rot.rots);
   sd->wm_rot.rots = NULL;

   /* Don't let callback in the air that point to sd */
   if (sd->ee)
     {
        ecore_evas_callback_mouse_in_set(sd->ee, NULL);
        ecore_evas_callback_focus_in_set(sd->ee, NULL);
        ecore_evas_callback_focus_out_set(sd->ee, NULL);
        ecore_evas_callback_move_set(sd->ee, NULL);
        ecore_evas_callback_state_change_set(sd->ee, NULL);
        ecore_evas_callback_pre_render_set(sd->ee, NULL);
     }

   efl_canvas_group_del(efl_super(obj, MY_CLASS));

   if (eina_value_type_get(&sd->exit_on_close))
     efl_loop_quit(efl_loop_get(obj), sd->exit_on_close);
   else if (!_elm_win_list)
     {
       if (elm_policy_get(ELM_POLICY_QUIT) == ELM_POLICY_QUIT_LAST_WINDOW_CLOSED)
         _elm_win_flush_cache_and_exit(obj);
     }
   if (!_elm_win_list)
     {
        efl_event_callback_call(efl_app_main_get(), EFL_APP_EVENT_STANDBY, NULL);
        if (eina_value_type_get(&exit_on_all_windows_closed))
          efl_loop_quit(efl_loop_get(obj), exit_on_all_windows_closed);
     }
}

EOLIAN static void
_efl_ui_win_efl_gfx_entity_position_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Position2D pos)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_MOVE, 0, pos.x, pos.y))
     return;

   if (sd->img_obj)
     {
        if ((pos.x != sd->screen.x) || (pos.y != sd->screen.y))
          {
             sd->screen.x = pos.x;
             sd->screen.y = pos.y;
             efl_event_callback_call(obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, &pos);
             evas_object_smart_callback_call(obj, "move", NULL);
          }
        goto super_skip;
     }
   else
     {
        if (!sd->response)
          {
             sd->req_xy = EINA_TRUE;
             sd->req_x = pos.x;
             sd->req_y = pos.y;
             TRAP(sd, move, pos.x, pos.y);
          }
        if (!ecore_evas_override_get(sd->ee)) goto super_skip;
     }

   efl_gfx_entity_position_set(efl_super(obj, MY_CLASS), pos);

   if (ecore_evas_override_get(sd->ee))
     {
        sd->screen.x = pos.x;
        sd->screen.y = pos.y;
        efl_event_callback_call(obj, EFL_GFX_ENTITY_EVENT_POSITION_CHANGED, &pos);
        evas_object_smart_callback_call(obj, "move", NULL);
     }
   if (sd->frame_obj)
     {
#ifdef HAVE_ELEMENTARY_WL2
        /* TODO */
        /* ecore_wl_window_update_location(sd->wl.win, x, y); */
#endif
        sd->screen.x = pos.x;
        sd->screen.y = pos.y;
     }
   if (sd->img_obj)
     {
        sd->screen.x = pos.x;
        sd->screen.y = pos.y;
     }

   return;

super_skip:
   /* FIXME FIXME FIXME
    * Ugly code flow: legacy code had an early return in smart_move, ie.
    * evas object move would be processed but smart object move would be
    * aborted. This super call tries to simulate that. */
   efl_gfx_entity_position_set(efl_super(obj, EFL_CANVAS_GROUP_CLASS), pos);
}

EOLIAN static void
_efl_ui_win_efl_gfx_entity_size_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Size2D sz)
{
   if (_evas_object_intercept_call(obj, EVAS_OBJECT_INTERCEPT_CB_RESIZE, 0, sz.w, sz.h))
     return;

   if (sd->img_obj)
     {
        if (sd->constrain)
          {
             int sw, sh;

             ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sw, &sh);
             sz.w = MIN(sz.w, sw);
             sz.h = MIN(sz.h, sh);
          }
        if (sz.w < 1) sz.w = 1;
        if (sz.h < 1) sz.h = 1;

        evas_object_image_size_set(sd->img_obj, sz.w, sz.h);
     }

   _elm_win_frame_obj_update(sd, 1);
   if (!sd->response)
     {
        sd->req_wh = EINA_TRUE;
        sd->req_w = sz.w;
        sd->req_h = sz.h;
        TRAP(sd, resize, sz.w, sz.h);
     }

   efl_gfx_entity_size_set(efl_super(obj, MY_CLASS), sz);
   /* if window is hidden during a resize,
    * revert to initial state where pre-render triggers recalc and other resizes are deferred
    */
   if (efl_gfx_entity_visible_get(obj)) return;
   if (!sd->first_draw) return;
   sd->first_draw = EINA_FALSE;
   edje_object_freeze(sd->frame_obj);
}

static void
_elm_win_delete_request(Ecore_Evas *ee)
{
   Efl_Ui_Win_Data *sd = _elm_win_associate_get(ee);
   Evas_Object *obj;

   if (!sd) return;

   obj = sd->obj;

   int autodel = sd->autodel;
   sd->autodel_clear = &autodel;
   evas_object_ref(obj);
   efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_DELETE_REQUEST, NULL);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   if (sd->autohide)
     evas_object_hide(obj);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   if (_elm_config->atspi_mode)
     efl_access_window_destroyed_signal_emit(obj);
   ELM_WIN_DATA_ALIVE_CHECK(obj, sd);
   if (autodel) evas_object_del(obj);
   else sd->autodel_clear = NULL;
   evas_object_unref(obj);
}

#ifdef HAVE_ELEMENTARY_WL2
static void
_elm_win_wlwindow_get(Efl_Ui_Win_Data *sd)
{
   Ecore_Wl2_Window *pwin = sd->wl.win;
   sd->wl.win = _elm_ee_wlwin_get(sd->ee);
   if (sd->wl.win != pwin)
     {
        char buf[128];
        int id;

        snprintf(buf, sizeof(buf), "%u||%p", getpid(), sd->wl.win);
        eina_stringshare_replace(&sd->stack_id, buf);
        id = ecore_evas_aux_hint_id_get(sd->ee, "stack_id");
        if (id >= 0) ecore_evas_aux_hint_val_set(sd->ee, id, sd->stack_id);
        else ecore_evas_aux_hint_add(sd->ee, "stack_id", sd->stack_id);
     }
}

void
_elm_win_wl_cursor_set(Evas_Object *obj, const char *cursor)
{
   ELM_WIN_DATA_GET(obj, sd);

   if (!sd) return;

   if (sd->pointer.obj)
     {
        Evas_Coord mw = 1, mh = 1, hx = 0, hy = 0;

        if (cursor)
          {
             if (elm_widget_theme_object_set(sd->obj, sd->pointer.obj,
                                        "cursor", cursor, "default"))
               {
                  elm_widget_theme_object_set(sd->obj, sd->pointer.obj,
                                        "pointer", "base", "default");
               }
          }
        else
          elm_widget_theme_object_set(sd->obj, sd->pointer.obj,
                                "pointer", "base", "default");

        edje_object_size_min_get(sd->pointer.obj, &mw, &mh);
        edje_object_size_min_restricted_calc(sd->pointer.obj, &mw, &mh, mw, mh);
        if ((mw < 32) || (mh < 32))
          {
             mw = 32;
             mh = 32;
          }
        evas_object_geometry_set(sd->pointer.obj, 0, 0, mw, mh);
        if (elm_widget_is_legacy(obj))
          edje_object_part_geometry_get(sd->pointer.obj,
                                        "elm.swallow.hotspot",
                                        &hx, &hy, NULL, NULL);
        else
          edje_object_part_geometry_get(sd->pointer.obj,
                                        "efl.hotspot",
                                        &hx, &hy, NULL, NULL);

        sd->pointer.hot_x = hx;
        sd->pointer.hot_y = hy;

        ecore_evas_resize(sd->pointer.ee, mw, mh);
     }

   if ((sd->wl.win) && (sd->pointer.surf) && (sd->pointer.visible))
     {
        /* FIXME: multiseat */
        Ecore_Wl2_Input *input;
        Eina_Iterator *it;

        /* FIXME: Here be dragons...
           pointer_set_cursor is totally unsynchronized, and on a cursor
           change we get here before the new cursor is rendered.  So
           the cursor frequently moves to its new hotspot with the old
           cursor image, causing an ugly jump.
           Forcing manual render causes us to render first then set the
           cursor, which is still racey but far more likely to win the
           race.
           The right way to do this is to create an entirely new surface
           on every cursor change.
         */
        ecore_evas_manual_render(sd->pointer.ee);
        it = ecore_wl2_display_inputs_get(ecore_wl2_window_display_get(sd->wl.win));
        EINA_ITERATOR_FOREACH(it, input)
          ecore_wl2_input_pointer_set(input, sd->pointer.surf, sd->pointer.hot_x, sd->pointer.hot_y);
        eina_iterator_free(it);
     }
}
#endif

Ecore_Cocoa_Window *
_elm_ee_cocoa_win_get(const Ecore_Evas *ee)
{
#ifdef HAVE_ELEMENTARY_COCOA
   const char *engine_name;

   if (!ee) return NULL;

   engine_name = ecore_evas_engine_name_get(ee);
   if (EINA_UNLIKELY(!engine_name)) return NULL;

   if (!strcmp(engine_name, "opengl_cocoa") ||
       !strcmp(engine_name, "gl_cocoa"))
     return ecore_evas_cocoa_window_get(ee);
#else
   (void)ee;
#endif
   return NULL;
}

Ecore_Win32_Window *
_elm_ee_win32win_get(const Ecore_Evas *ee)
{
#ifdef HAVE_ELEMENTARY_WIN32
   const char *engine_name;

   if (!ee) return NULL;

   engine_name = ecore_evas_engine_name_get(ee);
   if (EINA_UNLIKELY(!engine_name)) return NULL;

   if ((!strcmp(engine_name, ELM_SOFTWARE_WIN32)) ||
       (!strcmp(engine_name, ELM_SOFTWARE_DDRAW)))
     {
        return ecore_evas_win32_window_get(ee);
     }
#else
   (void)ee;
#endif
   return NULL;
}

#ifdef HAVE_ELEMENTARY_COCOA
static void
_elm_win_cocoawindow_get(Efl_Ui_Win_Data *sd)
{
   sd->cocoa.win = _elm_ee_cocoa_win_get(sd->ee);
}
#endif

#ifdef HAVE_ELEMENTARY_WIN32
static void
_internal_elm_win_win32window_get(Efl_Ui_Win_Data *sd)
{
   sd->win32.win = _elm_ee_win32win_get(sd->ee);
}
#endif

#ifdef HAVE_ELEMENTARY_X
static void
_elm_win_xwin_update(Efl_Ui_Win_Data *sd)
{
   const char *s;

   if (sd->type == EFL_UI_WIN_TYPE_FAKE) return;
   _internal_elm_win_xwindow_get(sd);

   if (!sd->x.xwin) return;  /* nothing more to do */
   _internal_elm_win_xwindow_get(sd);

   if (sd->stack_master_id)
     {
        Ecore_X_Window win = strtol(sd->stack_master_id, NULL, 16);
        if (win)
          {
             ecore_x_icccm_transient_for_set(sd->x.xwin, win);
             if (sd->stack_base)
               ecore_x_e_stack_type_set(sd->x.xwin, ECORE_X_STACK_BASE);
             else
               ecore_x_e_stack_type_set(sd->x.xwin, ECORE_X_STACK_STANDARD);
          }
     }
   else
     {
        if (sd->parent)
          {
             ELM_WIN_DATA_GET(sd->parent, sdp);
             if (sdp)
               {
                  _internal_elm_win_xwindow_get(sdp);
                  ecore_x_icccm_transient_for_set(sd->x.xwin, sdp->x.xwin);
               }
          }
     }

   s = sd->title;
   if (!s) s = _elm_appname;
   if (!s) s = "";
   if (sd->icon_name) s = sd->icon_name;
   ecore_x_icccm_icon_name_set(sd->x.xwin, s);
   ecore_x_netwm_icon_name_set(sd->x.xwin, s);

   s = sd->role;
   if (s) ecore_x_icccm_window_role_set(sd->x.xwin, s);

   // set window icon
   if (sd->icon)
     {
        Eo *image = NULL;

        if (efl_isa(sd->icon, EFL_CANVAS_IMAGE_INTERNAL_CLASS))
          image = sd->icon;
        else if (efl_isa(sd->icon, EFL_UI_IMAGE_CLASS))
          image = elm_image_object_get(sd->icon);

        if (image)
          {
             int w = 0, h = 0, stride = 0, x, y;
             Eina_Bool unmap = EINA_FALSE;
             Eina_Rw_Slice sl = { 0 };

             if (efl_isa(image, EFL_CANVAS_IMAGE_CLASS))
               {
                  Eina_Rect rect = { 0 };

                  unmap = EINA_TRUE;
                  rect.size = efl_gfx_buffer_size_get(image);
                  sl = efl_gfx_buffer_map(image, EFL_GFX_BUFFER_ACCESS_MODE_READ,
                                          &rect, EFL_GFX_COLORSPACE_ARGB8888, 0,
                                          &stride);
                  w = rect.w;
                  h = rect.h;
               }
             else
               {
                  evas_object_image_size_get(image, &w, &h);
                  stride = evas_object_image_stride_get(image);
                  sl.mem = evas_object_image_data_get(image, EINA_FALSE);
               }

             if (sl.mem)
               {
                  Ecore_X_Icon ic;

                  ic.width = w;
                  ic.height = h;
                  if ((w > 0) && (h > 0) &&
                      (stride >= (int)(w * sizeof(unsigned int))))
                    {
                       if (stride == (int)(w * sizeof(unsigned int)))
                         {
                            ic.data = sl.mem;
                            ecore_x_netwm_icons_set(sd->x.xwin, &ic, 1);
                         }
                       else
                         {
                            ic.data = malloc(w * h * sizeof(unsigned int));
                            if (ic.data)
                              {
                                 unsigned char *p = sl.mem;
                                 unsigned int *p2 = ic.data;

                                 for (y = 0; y < h; y++)
                                   {
                                      for (x = 0; x < w; x++)
                                        {
                                           *p2 = *((unsigned int *)p);
                                           p += sizeof(unsigned int);
                                           p2++;
                                        }
                                      p += (stride - (w * sizeof(unsigned int)));
                                   }
                                 ecore_x_netwm_icons_set(sd->x.xwin, &ic, 1);
                                 free(ic.data);
                              }
                         }
                    }
                  if (unmap) efl_gfx_buffer_unmap(image, sl);
                  else evas_object_image_data_set(image, sl.mem);
               }
          }
     }

   switch (sd->type)
     {
      case ELM_WIN_BASIC:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_NORMAL);
        break;

      case ELM_WIN_DIALOG_BASIC:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DIALOG);
        break;

      case ELM_WIN_DESKTOP:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DESKTOP);
        break;

      case ELM_WIN_DOCK:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DOCK);
        break;

      case ELM_WIN_TOOLBAR:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_TOOLBAR);
        break;

      case ELM_WIN_MENU:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_MENU);
        break;

      case ELM_WIN_UTILITY:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_UTILITY);
        break;

      case ELM_WIN_SPLASH:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_SPLASH);
        break;

      case ELM_WIN_DROPDOWN_MENU:
        ecore_x_netwm_window_type_set
          (sd->x.xwin, ECORE_X_WINDOW_TYPE_DROPDOWN_MENU);
        break;

      case ELM_WIN_POPUP_MENU:
        ecore_x_netwm_window_type_set
          (sd->x.xwin, ECORE_X_WINDOW_TYPE_POPUP_MENU);
        break;

      case ELM_WIN_TOOLTIP:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_TOOLTIP);
        ecore_x_window_shape_input_rectangle_set(sd->x.xwin, 0, 0, 0, 0);
        break;

      case ELM_WIN_NOTIFICATION:
        ecore_x_netwm_window_type_set
          (sd->x.xwin, ECORE_X_WINDOW_TYPE_NOTIFICATION);
        break;

      case ELM_WIN_COMBO:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_COMBO);
        break;

      case ELM_WIN_DND:
        ecore_x_netwm_window_type_set(sd->x.xwin, ECORE_X_WINDOW_TYPE_DND);
        break;

      default:
        break;
     }
   ecore_x_e_virtual_keyboard_state_set
     (sd->x.xwin, (Ecore_X_Virtual_Keyboard_State)sd->kbdmode);
   if (sd->legacy.indmode == ELM_WIN_INDICATOR_SHOW)
     ecore_x_e_illume_indicator_state_set
       (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_ON);
   else if (sd->legacy.indmode == ELM_WIN_INDICATOR_HIDE)
     ecore_x_e_illume_indicator_state_set
       (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_OFF);

   if ((sd->wm_rot.count) && (sd->wm_rot.rots))
     ecore_evas_wm_rotation_available_rotations_set(sd->ee,
                                                    sd->wm_rot.rots,
                                                    sd->wm_rot.count);
   if (sd->wm_rot.preferred_rot != -1)
     ecore_evas_wm_rotation_preferred_rotation_set(sd->ee,
                                                   sd->wm_rot.preferred_rot);

#ifdef HAVE_ELEMENTARY_X
   const char *engine_name = ecore_evas_engine_name_get(sd->ee);
   if (engine_name) _elm_win_need_frame_adjust(sd, engine_name);
   if (sd->csd.need && sd->x.xwin)
     {
        TRAP(sd, borderless_set, EINA_TRUE);
        ecore_x_mwm_borderless_set(sd->x.xwin, EINA_TRUE);
     }
#endif
}

#endif

/**
  * @internal
  *
  * Resize the window according to window layout's min and weight.
  * If the window layout's weight is 0.0, the window max is limited to layout's
  * min size.
  *
  * This is called when the window layout's weight hint is changed or when the
  * window is rotated.
  *
  * @param obj window object
  */
static void
_elm_win_resize_objects_eval(Evas_Object *obj, Eina_Bool force_resize)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_get(obj, MY_CLASS);
   Evas_Coord w, h, minw, minh, maxw, maxh, ow, oh;
   Eina_Bool unresizable;
   double wx, wy;

   evas_object_size_hint_combined_min_get(sd->legacy.edje, &minw, &minh);
   if ((!minw) && (!minh) && (!sd->deferred_resize_job)) return;

   efl_gfx_hint_size_restricted_max_set(obj, EINA_SIZE2D(-1, -1));
   // If content has a weight, make resizable
   efl_gfx_hint_weight_get(sd->legacy.edje, &wx, &wy);

   // Content max hint is ignored
   maxw = sd->max_w;
   maxh = sd->max_h;

   // Compatibility hack (for E)
   if (sd->single_edje_content && EINA_DBL_EQ(wx, 0) && EINA_DBL_EQ(wy, 0))
     wx = wy = 1;

   if (EINA_DBL_EQ(wx, 0)) maxw = minw;
   if (maxw < 1) maxw = 32767;
   if (EINA_DBL_EQ(wy, 0)) maxh = minh;
   if (maxh < 1) maxh = 32767;
   if (maxw < minw) maxw = minw;
   if (maxh < minh) maxh = minh;
   if (maxw > 32767) maxw = 32767;
   if (maxh > 32767) maxh = 32767;

   unresizable = ((minw == maxw) && (minh == maxh));

   if (sd->csd.need_unresizable != unresizable)
     {
        sd->csd.need_unresizable = unresizable;
        _elm_win_frame_style_update(sd, 0, 1);
     }

   if (sd->frame_obj)
     {
        int fw, fh;

        evas_output_framespace_get(sd->evas, NULL, NULL, &fw, &fh);
//        minw += fw;
//        minh += fh;
//        maxw += fw;
//        maxh += fh;
     }

   sd->tmp_updating_hints = 1;
   efl_gfx_hint_size_restricted_min_set(obj, EINA_SIZE2D(minw, minh));
   efl_gfx_hint_size_restricted_max_set(obj, EINA_SIZE2D(maxw, maxh));
   sd->tmp_updating_hints = 0;
   _elm_win_size_hints_update(obj, sd);

   if (sd->deferred_resize_job)
     _elm_win_resize_job(sd->obj);

   /* do not need to go below. if you go, ee could become 0. */
   if ((!minw) && (!minh)) return;

   evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
   w = ow;
   h = oh;
   if (w < minw) w = minw;
   if (h < minh) h = minh;
   if (w > maxw) w = maxw;
   if (h > maxh) h = maxh;
   if (!force_resize && (w == ow) && (h == oh))
     return;

   sd->req_wh = EINA_FALSE;
   if (sd->img_obj) evas_object_resize(obj, w, h);
   else
     {
        _elm_win_frame_geometry_adjust(sd);
        if (!sd->response)
          {
             sd->req_wh = EINA_TRUE;
             sd->req_w = w;
             sd->req_h = h;
             TRAP(sd, resize, w, h);
          }
     }
}

static void
_elm_win_on_resize_obj_changed_size_hints(void *data,
                                          Evas *e EINA_UNUSED,
                                          Evas_Object *obj EINA_UNUSED,
                                          void *event_info EINA_UNUSED)
{
   _elm_win_resize_objects_eval(data, EINA_FALSE);
}

void
_elm_win_shutdown(void)
{
   while (_elm_win_list)
     {
        Eina_List *itr = _elm_win_list;
        evas_object_del(itr->data);
        if (_elm_win_list == itr)
          {
             _elm_win_list = eina_list_remove_list(_elm_win_list, _elm_win_list);
          }
     }
   ELM_SAFE_FREE(_elm_win_state_eval_timer, ecore_timer_del);
}

void
_elm_win_rescale(Elm_Theme *th,
                 Eina_Bool use_theme)
{
   const Eina_List *l;
   Evas_Object *obj;

   if (!use_theme)
     {
        EINA_LIST_FOREACH(_elm_win_list, l, obj)
          elm_widget_theme(obj);
     }
   else
     {
        EINA_LIST_FOREACH(_elm_win_list, l, obj)
          elm_widget_theme_specific(obj, th, EINA_FALSE);
     }
}

void
_elm_win_access(Eina_Bool is_access)
{
   Evas *evas;
   const Eina_List *l;
   Evas_Object *obj;
   Evas_Object *fobj;

   EINA_LIST_FOREACH(_elm_win_list, l, obj)
     {
        elm_widget_access(obj, is_access);

         /* floating orphan object. if there are A, B, C objects and user does
            as below, then there would be floating orphan objects.

              1. elm_object_content_set(layout, A);
              2. elm_object_content_set(layout, B);
              3. elm_object_content_set(layout, C);

            now, the object A and B are floating orphan objects */

        fobj = obj;
        for (;;)
          {
             fobj = evas_object_below_get(fobj);
             if (!fobj) break;

             if (elm_widget_is(fobj) && !elm_widget_parent_get(fobj))
               {
                  elm_widget_access(fobj, is_access);
               }
          }

        if (!is_access)
          {
             evas = evas_object_evas_get(obj);
            if (evas) _elm_access_object_highlight_disable(evas);
          }
     }
}

void
_elm_win_translate(void)
{
   const Eina_List *l;
   Evas_Object *obj;

   EINA_LIST_FOREACH(_elm_win_list, l, obj)
     efl_ui_l10n_translation_update(obj);
}


#ifdef HAVE_ELEMENTARY_X
static Eina_Bool
_elm_win_client_message(void *data,
                        int type EINA_UNUSED,
                        void *event)
{
   ELM_WIN_DATA_GET(data, sd);
   Ecore_X_Event_Client_Message *e = event;

   if (e->format != 32) return ECORE_CALLBACK_PASS_ON;
   _internal_elm_win_xwindow_get(sd);
   if (e->message_type == ECORE_X_ATOM_E_COMP_FLUSH)
     {
        if ((unsigned int)e->data.l[0] == sd->x.xwin)
          {
             Evas *evas = evas_object_evas_get(sd->obj);
             if (evas)
               {
                  edje_file_cache_flush();
                  edje_collection_cache_flush();
                  evas_image_cache_flush(evas);
                  evas_font_cache_flush(evas);
               }
          }
     }
   else if (e->message_type == ECORE_X_ATOM_E_COMP_DUMP)
     {
        if ((unsigned int)e->data.l[0] == sd->x.xwin)
          {
             Evas *evas = evas_object_evas_get(sd->obj);
             if (evas)
               {
                  edje_file_cache_flush();
                  edje_collection_cache_flush();
                  evas_image_cache_flush(evas);
                  evas_font_cache_flush(evas);
                  evas_render_dump(evas);
               }
          }
     }
   else if (e->message_type == ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL)
     {
        if ((unsigned int)e->data.l[0] == sd->x.xwin)
          {
             if ((unsigned int)e->data.l[1] ==
                 ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_NEXT)
               {
                  // XXX: call right access func
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_PREV)
               {
                  // XXX: call right access func
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_ACTIVATE)
               {
                  _elm_access_highlight_object_activate
                    (sd->obj, EFL_UI_ACTIVATE_DEFAULT);
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ)
               {
                  /* there would be better way to read highlight object */
                  Evas *evas;
                  evas = evas_object_evas_get(sd->obj);
                  if (!evas) return ECORE_CALLBACK_PASS_ON;

                  _elm_access_mouse_event_enabled_set(EINA_TRUE);

                  evas_event_feed_mouse_in(evas, 0, NULL);
                  evas_event_feed_mouse_move
                    (evas, e->data.l[2], e->data.l[3], 0, NULL);

                  _elm_access_mouse_event_enabled_set(EINA_FALSE);
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ_NEXT)
               {
                  _elm_access_highlight_cycle(sd->obj, ELM_FOCUS_NEXT);
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ_PREV)
               {
                  _elm_access_highlight_cycle(sd->obj, ELM_FOCUS_PREVIOUS);
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_UP)
               {
                  _elm_access_highlight_object_activate
                    (sd->obj, EFL_UI_ACTIVATE_UP);
               }
             else if ((unsigned int)e->data.l[1] ==
                      ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_DOWN)
               {
                  _elm_access_highlight_object_activate
                    (sd->obj, EFL_UI_ACTIVATE_DOWN);
               }
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_elm_win_property_change(void *data,
                         int type EINA_UNUSED,
                         void *event)
{
   ELM_WIN_DATA_GET(data, sd);
   Ecore_X_Event_Window_Property *e = event;

   if (e->atom == ECORE_X_ATOM_E_ILLUME_INDICATOR_STATE)
     {
        _internal_elm_win_xwindow_get(sd);
        if (e->win == sd->x.xwin)
          {
             sd->legacy.indmode = (Elm_Win_Indicator_Mode)ecore_x_e_illume_indicator_state_get(e->win);
             efl_event_callback_legacy_call
               (sd->obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL);
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}
#endif

#ifdef HAVE_ELEMENTARY_WIN32
static Eina_Bool
_elm_win_key_down(void *data,
                  int type EINA_UNUSED,
                  void *event)
{
   ELM_WIN_DATA_GET(data, sd);
   Ecore_Event_Key *e = event;
   if ((e->modifiers & ECORE_EVENT_MODIFIER_ALT) &&
       (strcmp(e->key, "F4") == 0))
     _elm_win_delete_request(sd->ee);

   return ECORE_CALLBACK_PASS_ON;
}

#endif

static void
_elm_win_focus_highlight_hide(void *data EINA_UNUSED,
                              Evas_Object *obj,
                              const char *emission EINA_UNUSED,
                              const char *source EINA_UNUSED)
{
   evas_object_hide(obj);
}

static void
_elm_win_focus_highlight_anim_end(void *data,
                                  Evas_Object *obj,
                                  const char *emission EINA_UNUSED,
                                  const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   _elm_win_focus_highlight_simple_setup(sd, obj);
}

static void
_elm_win_focus_highlight_init(Efl_Ui_Win_Data *sd)
{
   evas_event_callback_add(sd->evas, EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_IN,
                           _elm_win_object_focus_in, sd->obj);
   evas_event_callback_add(sd->evas,
                           EVAS_CALLBACK_CANVAS_OBJECT_FOCUS_OUT,
                           _elm_win_object_focus_out, sd->obj);

   sd->focus_highlight.cur.target = _elm_win_focus_target_get(evas_focus_get(sd->evas));
   if (sd->focus_highlight.cur.target)
     {
        if (elm_widget_highlight_in_theme_get(sd->focus_highlight.cur.target))
          sd->focus_highlight.cur.in_theme = EINA_TRUE;
        else
          _elm_win_focus_target_callbacks_add(sd);

        evas_object_event_callback_add
           (sd->focus_highlight.cur.target,
            EVAS_CALLBACK_DEL, _elm_win_focus_target_del, sd->obj);
     }

   sd->focus_highlight.prev.target = NULL;
   sd->focus_highlight.theme_changed = EINA_TRUE;
   if (!sd->focus_highlight.fobj)
     {
        sd->focus_highlight.fobj = edje_object_add(sd->evas);

        if (elm_widget_is_legacy(sd->obj))
          {
             edje_object_signal_callback_add(sd->focus_highlight.fobj,
                                             "elm,action,focus,hide,end", "*",
                                             _elm_win_focus_highlight_hide, NULL);
             edje_object_signal_callback_add(sd->focus_highlight.fobj,
                                             "elm,action,focus,anim,end", "*",
                                             _elm_win_focus_highlight_anim_end, sd->obj);
          }
        else
          {
             edje_object_signal_callback_add(sd->focus_highlight.fobj,
                                             "efl,focus,visible,off,done", "*",
                                             _elm_win_focus_highlight_hide, NULL);
             edje_object_signal_callback_add(sd->focus_highlight.fobj,
                                             "efl,action,focus,anim,end", "*",
                                             _elm_win_focus_highlight_anim_end, sd->obj);
          }
     }

   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

typedef struct _resize_info {
   const char *source;
   const char *cursor;
   Efl_Ui_Win_Move_Resize_Mode mode;
   int wl_location;
#ifdef HAVE_ELEMENTARY_X
#define XDIR(d) , ECORE_X_NETWM_DIRECTION_##d
   Ecore_X_Netwm_Direction x_dir;
#else
# define XDIR(d)
#endif
} resize_info;

static const resize_info _resize_infos_legacy[8] = {
   { "elm.event.resize.t",  ELM_CURSOR_TOP_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP, 1 XDIR(SIZE_T) },
   { "elm.event.resize.b",  ELM_CURSOR_BOTTOM_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM, 2 XDIR(SIZE_B) },
   { "elm.event.resize.l",  ELM_CURSOR_LEFT_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 4 XDIR(SIZE_L) },
   { "elm.event.resize.r",  ELM_CURSOR_RIGHT_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 8 XDIR(SIZE_R) },
   { "elm.event.resize.tl", ELM_CURSOR_TOP_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 5 XDIR(SIZE_TL) },
   { "elm.event.resize.bl", ELM_CURSOR_BOTTOM_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 6 XDIR(SIZE_BL) },
   { "elm.event.resize.br", ELM_CURSOR_BOTTOM_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 10 XDIR(SIZE_BR) },
   { "elm.event.resize.tr", ELM_CURSOR_TOP_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 9 XDIR(SIZE_TR) },
};

static const resize_info _resize_infos[8] = {
   { "efl.event.resize.t",  ELM_CURSOR_TOP_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP, 1 XDIR(SIZE_T) },
   { "efl.event.resize.b",  ELM_CURSOR_BOTTOM_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM, 2 XDIR(SIZE_B) },
   { "efl.event.resize.l",  ELM_CURSOR_LEFT_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 4 XDIR(SIZE_L) },
   { "efl.event.resize.r",  ELM_CURSOR_RIGHT_SIDE, EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 8 XDIR(SIZE_R) },
   { "efl.event.resize.tl", ELM_CURSOR_TOP_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 5 XDIR(SIZE_TL) },
   { "efl.event.resize.bl", ELM_CURSOR_BOTTOM_LEFT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT, 6 XDIR(SIZE_BL) },
   { "efl.event.resize.br", ELM_CURSOR_BOTTOM_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 10 XDIR(SIZE_BR) },
   { "efl.event.resize.tr", ELM_CURSOR_TOP_RIGHT_CORNER, EFL_UI_WIN_MOVE_RESIZE_MODE_TOP | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT, 9 XDIR(SIZE_TR) },
};

static inline Efl_Ui_Win_Move_Resize_Mode
_move_resize_mode_rotate(int rotation, Efl_Ui_Win_Move_Resize_Mode mode)
{
   const Efl_Ui_Win_Move_Resize_Mode edges[4] = {
      EFL_UI_WIN_MOVE_RESIZE_MODE_TOP,    EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT,
      EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM, EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT
   };

   const Efl_Ui_Win_Move_Resize_Mode corners[4] = {
      EFL_UI_WIN_MOVE_RESIZE_MODE_TOP    | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT,
      EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_LEFT,
      EFL_UI_WIN_MOVE_RESIZE_MODE_BOTTOM | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT,
      EFL_UI_WIN_MOVE_RESIZE_MODE_TOP    | EFL_UI_WIN_MOVE_RESIZE_MODE_RIGHT,
   };

   const int i = rotation / 90;
   int k;

   for (k = 0; k < 4; k++)
     if (mode == edges[k])
       return edges[(k + i) % 4];

   for (k = 0; k < 4; k++)
     if (mode == corners[k])
       return corners[(k + i) % 4];

   return EFL_UI_WIN_MOVE_RESIZE_MODE_MOVE;
}

static const resize_info *
_resize_info_get(Evas_Object *obj, int rotation, Efl_Ui_Win_Move_Resize_Mode mode)
{
   if (rotation)
     return _resize_info_get(obj, 0, _move_resize_mode_rotate(rotation, mode));

   if (elm_widget_is_legacy(obj))
     {
        for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos_legacy); k++)
          {
             if (_resize_infos_legacy[k].mode == mode)
               return &_resize_infos_legacy[k];
          }
     }
   else
     {
        for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++)
          {
             if (_resize_infos[k].mode == mode)
               return &_resize_infos[k];
          }
     }

   return NULL;
}

static Efl_Ui_Win_Move_Resize_Mode
_move_resize_mode_get(Evas_Object *obj, const char *source)
{
   if (elm_widget_is_legacy(obj))
     {
        for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos_legacy); k++)
          if (!strcmp(source, _resize_infos_legacy[k].source))
            return _resize_infos_legacy[k].mode;
     }
   else
     {
        for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++)
          if (!strcmp(source, _resize_infos[k].source))
            return _resize_infos[k].mode;
     }

   return EFL_UI_WIN_MOVE_RESIZE_MODE_MOVE;
}

static void
_elm_win_frame_obj_move(void *data,
                        Evas *e EINA_UNUSED,
                        Evas_Object *obj EINA_UNUSED,
                        void *event_info EINA_UNUSED)
{
   Efl_Ui_Win_Data *sd;

   if (!(sd = data)) return;
   if (!sd->legacy.edje) return;

   _elm_win_frame_obj_update(sd, 0);
}

static void
_elm_win_frame_obj_resize(void *data,
                          Evas *e EINA_UNUSED,
                          Evas_Object *obj EINA_UNUSED,
                          void *event_info EINA_UNUSED)
{
   Efl_Ui_Win_Data *sd;

   if (!(sd = data)) return;
   if (!sd->legacy.edje) return;
   if (sd->ignore_frame_resize > 0) return;

   _elm_win_frame_obj_update(sd, 0);
}

static void
_elm_win_frame_cb_resize_show(void *data,
                              Evas_Object *obj EINA_UNUSED,
                              const char *sig EINA_UNUSED,
                              const char *source)
{
   ELM_WIN_DATA_GET(data, sd);

   if (!sd) return;
   if (sd->resizing) return;

#ifdef HAVE_ELEMENTARY_WL2
   if (sd->pointer.obj)
     {
        Efl_Ui_Win_Move_Resize_Mode mode = _move_resize_mode_get(sd->obj, source);
        const resize_info *ri = _resize_info_get(sd->obj, sd->rot, mode);
        if (ri) elm_widget_theme_object_set(sd->obj, sd->pointer.obj, "pointer", "base", ri->cursor);
     }
#else
   (void)source;
#endif
}

static void
_elm_win_frame_cb_resize_hide(void *data,
                              Evas_Object *obj EINA_UNUSED,
                              const char *sig EINA_UNUSED,
                              const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   if (!sd) return;
   if (sd->resizing) return;

#ifdef HAVE_ELEMENTARY_WL2
   if (sd->pointer.obj)
     elm_widget_theme_object_set(sd->obj, sd->pointer.obj,
                           "pointer", "base", "default");
#endif
}

#ifdef HAVE_ELEMENTARY_WL2
/* This only works when called from an edje event
 * that propagates seat info...
 */
static Ecore_Wl2_Input *
_elm_win_wayland_input_get(Efl_Ui_Win_Data *sd)
{
   Ecore_Wl2_Display *di;
   char *sname;
   const char *engine;

   engine = ecore_evas_engine_name_get(sd->ee);
   if (strcmp(engine, ELM_WAYLAND_SHM) &&
       strcmp(engine, ELM_WAYLAND_EGL))
     return NULL;

   di = ecore_wl2_window_display_get(sd->wl.win);

   sname = edje_object_signal_callback_seat_data_get();
   if (!sname) return NULL;
   return ecore_wl2_display_input_find_by_name(di, sname);
}
#endif

static inline Eina_Bool
_win_move_start(Efl_Ui_Win_Data *sd)
{
#ifdef HAVE_ELEMENTARY_WL2
   if (sd->wl.win)
     {
        Ecore_Wl2_Input *ei;

        ei = _elm_win_wayland_input_get(sd);
        ecore_wl2_window_move(sd->wl.win, ei);
        return EINA_TRUE;
     }
#endif

#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     {
        int x, y;

        _internal_elm_win_xwindow_get(sd);
        sd->resizing = EINA_TRUE;
        ecore_x_pointer_ungrab();
        ecore_x_pointer_root_xy_get(&x, &y);
        ecore_x_netwm_moveresize_request_send(sd->x.xwin, x, y,
                                              ECORE_X_NETWM_DIRECTION_MOVE, 1);
        return EINA_TRUE;
     }
#endif

   INF("Window move request not supported for this window!");
   return EINA_FALSE;
}

static Eina_Bool
_win_move_resize_start(Efl_Ui_Win_Data *sd, Efl_Ui_Win_Move_Resize_Mode mode)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
   const resize_info *ri;

   // 2. check move_resize already started
   if (sd->resizing)
     {
        ERR("Window is already being resized.");
        return EINA_FALSE;
     }

   if (mode == EFL_UI_WIN_MOVE_RESIZE_MODE_MOVE)
     return _win_move_start(sd);

   ri = _resize_info_get(sd->obj, sd->rot, mode);
   if (!ri)
     {
        ERR("Unsupported move_resize mode %#x", (int) mode);
        return EINA_FALSE;
     }

   sd->resizing = EINA_TRUE;

#ifdef HAVE_ELEMENTARY_WL2
   if (sd->wl.win)
     {
        Ecore_Wl2_Input *ei;

        ei = _elm_win_wayland_input_get(sd);
        ecore_wl2_window_resize(sd->wl.win, ei, ri->wl_location);
        return EINA_TRUE;
     }
#endif

#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     {
        int x, y;
        _internal_elm_win_xwindow_get(sd);
        ecore_x_pointer_ungrab();
        ecore_x_pointer_root_xy_get(&x, &y);
        ecore_x_netwm_moveresize_request_send(sd->x.xwin, x, y, ri->x_dir, 1);
        return EINA_TRUE;
     }
#endif

   INF("Window resize request not supported for this window!");
   return EINA_FALSE;
}

static void
_elm_win_frame_cb_move_start(void *data,
                             Evas_Object *obj EINA_UNUSED,
                             const char *sig EINA_UNUSED,
                             const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET_OR_RETURN(data, sd);

   _win_move_resize_start(sd, EFL_UI_WIN_MOVE_RESIZE_MODE_MOVE);
}

static void
_elm_win_frame_cb_resize_start(void *data, Evas_Object *obj EINA_UNUSED,
                               const char *sig EINA_UNUSED, const char *source)
{
   ELM_WIN_DATA_GET_OR_RETURN(data, sd);
   Efl_Ui_Win_Move_Resize_Mode mode;

   mode = _move_resize_mode_get(sd->obj, source);
   if (mode == EFL_UI_WIN_MOVE_RESIZE_MODE_MOVE) return;

   _win_move_resize_start(sd, mode);
}

static void
_elm_win_frame_cb_minimize(void *data,
                           Evas_Object *obj EINA_UNUSED,
                           const char *sig EINA_UNUSED,
                           const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);

   if (!sd) return;
//   sd->minimized = EINA_TRUE;
   TRAP(sd, iconified_set, EINA_TRUE);
}

static void
_elm_win_frame_cb_maximize(void *data,
                           Evas_Object *obj EINA_UNUSED,
                           const char *sig EINA_UNUSED,
                           const char *source EINA_UNUSED)
{
   Eina_Bool value;
   ELM_WIN_DATA_GET(data, sd);

   if (!sd) return;
   if (sd->maximized) value = EINA_FALSE;
   else value = EINA_TRUE;

   efl_ui_win_maximized_set(sd->obj, value);
}

static void
_elm_win_frame_cb_menu(void *data,
                        Evas_Object *obj EINA_UNUSED,
                        const char *sig EINA_UNUSED,
                        const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);
#ifdef HAVE_ELEMENTARY_WL2
   Ecore_Wl2_Input *input;
   int x, y, wx, wy;

   if (!sd->wl.win) return;
   evas_pointer_canvas_xy_get(sd->evas, &x, &y);
   ecore_wl2_window_geometry_get(sd->wl.win, &wx, &wy, NULL, NULL);
   if (x < 0) x += wx;
   if (y < 0) y += wy;

   {
      Eina_Iterator *it;
      Ecore_Wl2_Display *display = ecore_wl2_window_display_get(sd->wl.win);
      it = ecore_wl2_display_inputs_get(display);
      EINA_ITERATOR_FOREACH(it, input) break;
      eina_iterator_free(it);
   }
   if (sd->wl.win->xdg_toplevel)
     {
        xdg_toplevel_show_window_menu(sd->wl.win->xdg_toplevel,
                                       ecore_wl2_input_seat_get(input), 0, x, y);
        ecore_wl2_display_flush(input->display);
     }
   else if (sd->wl.win->zxdg_toplevel)
     {
        zxdg_toplevel_v6_show_window_menu(sd->wl.win->zxdg_toplevel,
                                       ecore_wl2_input_seat_get(input), 0, x, y);
        ecore_wl2_display_flush(input->display);
     }
#else
   (void)sd;
#endif
}
static void
_elm_win_frame_cb_close(void *data,
                        Evas_Object *obj EINA_UNUSED,
                        const char *sig EINA_UNUSED,
                        const char *source EINA_UNUSED)
{
   ELM_WIN_DATA_GET(data, sd);
   Evas_Object *win;

   /* FIXME: After the current freeze, this should be handled differently.
    *
    * Ideally, we would want to mimic the X11 backend and use something
    * like ECORE_WL_EVENT_WINDOW_DELETE and handle the delete_request
    * inside of ecore_evas. That would be the 'proper' way, but since we are
    * in a freeze right now, I cannot add a new event value, or a new
    * event structure to ecore_wayland.
    *
    * So yes, this is a temporary 'stop-gap' solution which will be fixed
    * when the freeze is over, but it does fix a trac bug for now, and in a
    * way which does not break API or the freeze. - dh
    */

   if (!sd) return;

   win = sd->obj;

   int autodel = sd->autodel;
   sd->autodel_clear = &autodel;
   evas_object_ref(win);
   efl_event_callback_legacy_call(win, EFL_UI_WIN_EVENT_DELETE_REQUEST, NULL);
   if (sd->autohide)
     evas_object_hide(win);
   // FIXME: if above callback deletes - then the below will be invalid
   if (autodel) evas_object_del(win);
   else sd->autodel_clear = NULL;
   evas_object_unref(win);
}

#ifdef HAVE_ELEMENTARY_WL2
static Eina_Bool
_elm_win_wl_configure(void *data, int t EINA_UNUSED, void *event)
{
   Ecore_Wl2_Event_Window_Configure *ev = event;
   ELM_WIN_DATA_GET(data, sd);
   if (sd->wl.win != ev->win) return ECORE_CALLBACK_RENEW;

   if (sd->resizing && (!ev->edges)) sd->resizing = EINA_FALSE;
   return ECORE_CALLBACK_RENEW;
}

#endif

static inline void
_elm_object_part_cursor_set(Evas_Object *obj, Evas_Object *edj,
                            const char *part, const char *cursor)
{
   Evas_Object *sub;

   edje_object_freeze(edj);
   sub = (Evas_Object *)edje_object_part_object_get(edj, part);
   edje_object_thaw(edj);
   if (!sub) return;

   elm_object_sub_cursor_set(sub, obj, cursor);
}

static char *
_efl_system_theme_path_get(void)
{
   // Find the default theme from EFL install. Quite ugly.
   const char *sysdir;
   char *version;
   char path[PATH_MAX];
   int v;

   sysdir = elm_theme_system_dir_get();
   if (!sysdir) return NULL;

   eina_file_path_join(path, PATH_MAX, sysdir, "default.edj");
   version = edje_file_data_get(path, "version");
   v = version ? atoi(version) : 0;
   free(version);
   if (v < FRAME_OBJ_THEME_MIN_VERSION)
     {
        ERR("Default system theme is too old, something is wrong with your installation of EFL.");
        return NULL;
     }
   return strdup(path);
}

static void
_elm_win_frame_add(Efl_Ui_Win_Data *sd, const char *element, const char *style)
{
   Evas_Object *obj = sd->obj;
   int w, h, v;
   const char *version;

   if (sd->frame_obj) return;

   sd->frame_obj = edje_object_add(sd->evas);

   // Verify theme version. Border requires an exact theme API.
   version = elm_theme_data_get(elm_widget_theme_get(sd->obj), "version");
   v = version ? atoi(version) : 0;
   if (EINA_LIKELY(v >= FRAME_OBJ_THEME_MIN_VERSION))
     {
        if (elm_widget_theme_object_set
            (sd->obj, sd->frame_obj, "border", element, style) == EFL_UI_THEME_APPLY_ERROR_GENERIC)
          {
             ERR("Failed to set main border theme for the window.");
             ELM_SAFE_FREE(sd->frame_obj, evas_object_del);
             return;
          }

        // Verify border.edc version as well
        version = edje_object_data_get(sd->frame_obj, "version");
        v = version ? atoi(version) : 0;
     }

   if (v < FRAME_OBJ_THEME_MIN_VERSION)
     {
        // Theme compatibility
        const char *key  = "elm/border/base/default"; // FIXME?
        char *sys_theme;

        WRN("Selected theme does not support the required border theme API "
            "(version = %d, requires >= %d).",
            v, FRAME_OBJ_THEME_MIN_VERSION);
        sys_theme = _efl_system_theme_path_get();
        if (!sys_theme ||
            !edje_object_file_set(sd->frame_obj, sys_theme, key))
          {
             ERR("Failed to set main border theme for the window.");
             ELM_SAFE_FREE(sd->frame_obj, evas_object_del);
             free(sys_theme);
             return;
          }
        free(sys_theme);
     }

   edje_object_freeze(sd->frame_obj);
   /* Small hack: The special value 2 means this is the top frame object.
    * We propagate to the children now (the edc group contents), but subsequent
    * calls to smart_member_add will not propagate the flag further. Note that
    * this little hack will fall apart if edje creates and destroys objects on
    * the fly. */
   efl_canvas_object_is_frame_object_set(sd->frame_obj, 2);

   if (elm_widget_is_legacy(sd->obj))
     edje_object_part_swallow(sd->frame_obj, "elm.swallow.client", sd->legacy.edje);
   else
     edje_object_part_swallow(sd->frame_obj, "efl.client", sd->legacy.edje);

   if (sd->icon)
     evas_object_show(sd->icon);
   else
     {
        Eina_Bool set = EINA_FALSE;
        Eina_Bool legacy = elm_widget_is_legacy(sd->obj);

        if (legacy)
          sd->icon = elm_icon_add(sd->obj);
        else
          sd->icon = efl_add(EFL_UI_IMAGE_CLASS, sd->obj);

        if (sd->icon_name)
          {
             if (legacy)
               set = elm_icon_standard_set(sd->icon, sd->icon_name);
             else
               set = efl_ui_image_icon_set(sd->icon, sd->icon_name);
          }
        if (((!sd->icon_name) || (!set)) && _elm_appname)
          {
             Efreet_Desktop *d;
             d = efreet_util_desktop_exec_find(_elm_appname);
             if (d)
               {
                  if (legacy)
                    elm_icon_standard_set(sd->icon, d->icon);
                  else
                    efl_ui_image_icon_set(sd->icon, d->icon);
                  efreet_desktop_free(d);
               }
          }
        efl_access_object_access_type_set(sd->icon, EFL_ACCESS_TYPE_DISABLED);
     }

   if (elm_widget_is_legacy(sd->obj))
     edje_object_part_swallow(sd->frame_obj, "elm.swallow.icon", sd->icon);
   else
     edje_object_part_swallow(sd->frame_obj, "efl.icon", sd->icon);

   efl_canvas_object_is_frame_object_set(sd->icon, EINA_TRUE);

   evas_object_event_callback_add
     (sd->frame_obj, EVAS_CALLBACK_MOVE, _elm_win_frame_obj_move, sd);
   evas_object_event_callback_add
     (sd->frame_obj, EVAS_CALLBACK_RESIZE, _elm_win_frame_obj_resize, sd);

   if (elm_widget_is_legacy(sd->obj))
     {
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,move,start", "elm",
            _elm_win_frame_cb_move_start, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,resize,show", "*",
            _elm_win_frame_cb_resize_show, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,resize,hide", "*",
            _elm_win_frame_cb_resize_hide, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,resize,start", "*",
            _elm_win_frame_cb_resize_start, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,minimize", "elm",
            _elm_win_frame_cb_minimize, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,maximize", "elm",
            _elm_win_frame_cb_maximize, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,close", "elm",
            _elm_win_frame_cb_close, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "elm,action,menu", "elm",
            _elm_win_frame_cb_menu, obj);
     }
   else
     {
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,move,start", "efl",
            _elm_win_frame_cb_move_start, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,resize,show", "*",
            _elm_win_frame_cb_resize_show, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,resize,hide", "*",
            _elm_win_frame_cb_resize_hide, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,resize,start", "*",
            _elm_win_frame_cb_resize_start, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,minimize", "efl",
            _elm_win_frame_cb_minimize, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,maximize", "efl",
            _elm_win_frame_cb_maximize, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,close", "efl",
            _elm_win_frame_cb_close, obj);
        edje_object_signal_callback_add
           (sd->frame_obj, "efl,action,menu", "efl",
            _elm_win_frame_cb_menu, obj);
     }

   if (!sd->pointer.obj)
     {
        if (elm_widget_is_legacy(obj))
          {
             for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos_legacy); k++)
               {
                  const resize_info *ri = &_resize_infos_legacy[k];
                  _elm_object_part_cursor_set(obj, sd->frame_obj, ri->source, ri->cursor);
               }
          }
        else
          {
             for (size_t k = 0; k < EINA_C_ARRAY_LENGTH(_resize_infos); k++)
               {
                  const resize_info *ri = &_resize_infos[k];
                  _elm_object_part_cursor_set(obj, sd->frame_obj, ri->source, ri->cursor);
               }
          }
     }

   if (sd->title)
     {
        if (elm_widget_is_legacy(sd->obj))
          edje_object_part_text_escaped_set(sd->frame_obj, "elm.text.title", sd->title);
        else
          edje_object_part_text_escaped_set(sd->frame_obj, "efl.text.title", sd->title);
     }

   {
      // HACK: Force render mode of bg rect to COPY. This avoids render garbage.
      Eo *bgrect;
      if (elm_widget_is_legacy(sd->obj))
        bgrect = (Eo *) edje_object_part_object_get(sd->frame_obj, "elm.rect.background");
      else
        bgrect = (Eo *) edje_object_part_object_get(sd->frame_obj, "efl.rect.background");

      efl_canvas_object_render_op_set(bgrect, EFL_GFX_RENDER_OP_COPY);
   }

   if (sd->first_draw)
     edje_object_thaw(sd->frame_obj);
   if (!efl_finalized_get(obj)) return;
   _elm_win_frame_style_update(sd, 1, 1);
   _elm_win_frame_geometry_adjust(sd);
   ecore_evas_geometry_get(sd->ee, NULL, NULL, &w, &h);
   if ((w > 1) && (h > 1))
     {
        ecore_evas_resize(sd->ee, w, h);
     }
}

static void
_elm_win_frame_style_update(Efl_Ui_Win_Data *sd, Eina_Bool force_emit, Eina_Bool calc)
{
   Eina_Bool borderless, maximized, shadow, focus, bg_solid, menu, unresizable,
         alpha, bg_standard, indicator;
   Eina_Bool changed = EINA_FALSE;

   if (!sd->frame_obj)
     {
        if (!efl_finalized_get(sd->obj)) return;
        if (EINA_LIKELY(sd->type == EFL_UI_WIN_TYPE_FAKE))
          return;
        if (!_elm_config->win_no_border)
          CRI("Window has no frame object!");
        return;
     }

   if ((sd->type == ELM_WIN_INLINED_IMAGE) ||
       (sd->type == ELM_WIN_SOCKET_IMAGE) ||
       (sd->type == ELM_WIN_TOOLTIP) ||
       (sd->type == ELM_WIN_COMBO) ||
       (sd->type == ELM_WIN_MENU) ||
       (sd->type == ELM_WIN_POPUP_MENU))
     {
        sd->csd.need_shadow = EINA_FALSE;
        sd->csd.need_borderless = EINA_TRUE;
        sd->csd.need_unresizable = EINA_TRUE;
        sd->csd.need_menu = EINA_FALSE;
        sd->csd.need_indicator = EINA_FALSE;
     }
   else if (_elm_config->win_no_border)
     sd->csd.need_borderless = EINA_TRUE;
   else
     {
        sd->csd.need_shadow = sd->csd.need && (!sd->maximized);
     }

   alpha = sd->application_alpha || sd->theme_alpha;
   borderless = sd->csd.need_borderless || (!sd->csd.need) || sd->fullscreen;
   maximized = sd->maximized;
   shadow = sd->csd.need_shadow && (!sd->fullscreen) && (!sd->maximized) && (!borderless);
   if (alpha && borderless) shadow = 0;
#ifdef HAVE_ELEMENTARY_WL2
   if (sd->wl.win)
     focus = ecore_wl2_window_activated_get(sd->wl.win);
   else
#endif
   focus = ecore_evas_focus_get(sd->ee);
   bg_standard = sd->csd.need_bg_standard;
   unresizable = sd->csd.need_unresizable;
   menu = sd->csd.need_menu;
   indicator = sd->csd.need_indicator;
   bg_solid = sd->csd.need_bg_solid;
   /* Set background transparent if window supports alpha.
    * If alpha window does not emit signal to show background rectangle, then
    * the background color set by _efl_ui_win_part_color_set cannot be applied
    * because the background rectangle remains hidden.
    */
   if (alpha && bg_solid && !(sd->csd.cur_bg_solid))
     edje_object_color_class_set(sd->frame_obj, "elm/win/background", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0);

   /* FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME
    * At the moment, E Wayland uses SSD for its internal windows. Which means
    * we must hide the shadow if the borderless flag is set. "trap" here means
    * we are likely to be running inside E compositor.
    * FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME FIXME */
   if (trap && sd->csd.wayland && sd->csd.need_borderless)
     shadow = 0;

#define STATE_SET(state, s1, s2, s3) do { \
   if (force_emit || (state != sd->csd.cur_##state)) \
     { \
        const char *sig = state ? s1 : s2; \
        edje_object_signal_emit(sd->frame_obj, sig, s3); \
        DBG("frame style emit: %p %s", sd->obj, sig); \
        sd->csd.cur_##state = state; \
        changed = EINA_TRUE; \
     } } while (0)

   if (elm_widget_is_legacy(sd->obj))
     {
        STATE_SET(borderless, "elm,state,borderless,on", "elm,state,borderless,off", "elm");
        STATE_SET(shadow, "elm,state,shadow,on", "elm,state,shadow,off", "elm");
        STATE_SET(maximized, "elm,state,maximized", "elm,state,unmaximized", "elm");
        STATE_SET(focus, "elm,action,focus", "elm,action,unfocus", "elm");
        STATE_SET(bg_solid, "elm,state,background,solid,on", "elm,state,background,solid,off", "elm");
        STATE_SET(bg_standard, "elm,state,background,standard,on", "elm,state,background,standard,off", "elm");
        STATE_SET(unresizable, "elm,state,unresizable,on", "elm,state,unresizable,off", "elm");
        STATE_SET(menu, "elm,action,show_menu", "elm,action,hide_menu", "elm");
        STATE_SET(indicator, "elm,action,show_indicator", "elm,action,hide_indicator", "elm");
     }
   else
     {
        STATE_SET(borderless, "efl,state,borderless,on", "efl,state,borderless,off", "efl");
        STATE_SET(shadow, "efl,state,shadow,on", "efl,state,shadow,off", "efl");
        STATE_SET(maximized, "efl,state,maximized", "efl,state,unmaximized", "efl");
        STATE_SET(focus, "efl,action,focus", "efl,action,unfocus", "efl");
        STATE_SET(bg_solid, "efl,state,background,solid,on", "efl,state,background,solid,off", "efl");
        STATE_SET(bg_standard, "efl,state,background,standard,on", "efl,state,background,standard,off", "efl");
        STATE_SET(unresizable, "efl,state,unresizable,on", "efl,state,unresizable,off", "efl");
        STATE_SET(menu, "efl,action,show_menu", "efl,action,hide_menu", "efl");
        STATE_SET(indicator, "efl,action,show_indicator", "efl,action,hide_indicator", "efl");
     }

#undef STATE_SET

   if (changed)
     {
        if (calc || force_emit)
          edje_object_message_signal_process(sd->frame_obj);
        if (calc)
          evas_object_smart_calculate(sd->frame_obj);
        _elm_win_frame_obj_update(sd, 0);
     }
}

#ifdef ELM_DEBUG
static void
_debug_key_down(void *data EINA_UNUSED,
                Evas *e EINA_UNUSED,
                Evas_Object *obj,
                void *event_info)
{
   Evas_Event_Key_Down *ev = event_info;

   if (ev->event_flags & EVAS_EVENT_FLAG_ON_HOLD)
     return;

   if ((strcmp(ev->key, "F12")) ||
       (!evas_key_modifier_is_set(ev->modifiers, "Control")))
     return;

   INF("Tree graph generated.");
   elm_object_tree_dot_dump(obj, "./dump.dot");
}

#endif

static void
_win_inlined_image_set(Efl_Ui_Win_Data *sd)
{
   evas_object_image_alpha_set(sd->img_obj, EINA_FALSE);
   evas_object_image_filled_set(sd->img_obj, EINA_TRUE);

   evas_object_event_callback_add
     (sd->img_obj, EVAS_CALLBACK_DEL, _elm_win_on_img_obj_del, sd->obj);
   evas_object_event_callback_add
     (sd->img_obj, EVAS_CALLBACK_HIDE, _win_img_hide, sd->obj);
   evas_object_event_callback_add
     (sd->img_obj, EVAS_CALLBACK_MOUSE_UP, _win_img_mouse_up, sd->obj);
   evas_object_event_callback_add
     (sd->img_obj, EVAS_CALLBACK_FOCUS_IN, _win_img_focus_in, sd->obj);
   evas_object_event_callback_add
     (sd->img_obj, EVAS_CALLBACK_FOCUS_OUT, _win_img_focus_out, sd->obj);
}

static void
_elm_win_on_icon_del(void *data, const Efl_Event *ev)
{
   ELM_WIN_DATA_GET(data, sd);

   if (sd->icon == ev->object) sd->icon = NULL;
}

EOLIAN static void
_efl_ui_win_efl_canvas_group_group_add(Eo *obj, Efl_Ui_Win_Data *_pd EINA_UNUSED)
{
   efl_canvas_group_add(efl_super(obj, MY_CLASS));

   elm_widget_theme_klass_set(obj, "win");
   elm_widget_can_focus_set(obj, EINA_TRUE);

   elm_widget_highlight_ignore_set(obj, EINA_TRUE);
}

#ifdef HAVE_ELEMENTARY_X
static void
_elm_x_io_err(void *data EINA_UNUSED)
{
   Eina_List *l;
   Evas_Object *obj;

   EINA_LIST_FOREACH(_elm_win_list, l, obj)
     evas_object_smart_callback_call(obj, "ioerr", NULL);
   fprintf(stderr, "X I/O Error - fatal. Exiting\n");
   exit(101);
}
#endif

static void
_elm_win_cb_hide(void *data EINA_UNUSED,
                 Evas *e EINA_UNUSED,
                 Evas_Object *obj EINA_UNUSED,
                 void *event_info EINA_UNUSED)
{
   _elm_win_state_eval_queue(EINA_FALSE);
}

static void
_elm_win_cb_show(void *data EINA_UNUSED,
                 Evas *e EINA_UNUSED,
                 Evas_Object *obj EINA_UNUSED,
                 void *event_info EINA_UNUSED)
{
   _elm_win_state_eval_queue(EINA_TRUE);
}

static inline Eina_Bool
_efl_ui_win_accel(Efl_Ui_Win_Data *sd, Eina_Stringshare **accel, int *gl_depth, int *gl_stencil, int *gl_msaa)
{
   const char *str = sd->accel_pref;
   const char *env;
   const char *cfg = NULL;
   Eina_Bool is_accel = EINA_FALSE;

   /* current elm config OR global override */
   if ((!str) || ((_elm_config->accel_override) && (_elm_config->accel)))
     {
        if (_elm_config->accel) cfg = _elm_config->accel;
        if (_elm_config->accel_override && _elm_accel_preference) cfg = _elm_accel_preference;
     }

   /* env var wins */
   env = getenv("ELM_ACCEL");

   if (env)
     is_accel = _elm_config_accel_preference_parse(env, accel, gl_depth, gl_stencil, gl_msaa);
   else if (cfg)
     {
        is_accel = !!cfg;
        *accel = eina_stringshare_ref(cfg);
        *gl_depth = _elm_config->gl_depth;
        *gl_stencil = _elm_config->gl_stencil;
        *gl_msaa = _elm_config->gl_msaa;
     }
   else
     is_accel = _elm_config_accel_preference_parse(str, accel, gl_depth, gl_stencil, gl_msaa);

   return is_accel;
}

static inline void
_elm_win_need_frame_adjust(Efl_Ui_Win_Data *sd, const char *engine)
{
   const char *s;

   /* this is for debug only - don't keep forever, it's not an api! */
   s = getenv("EFL_WIN_FRAME_MODE");
   sd->csd.wayland = (eina_streq(engine, ELM_WAYLAND_SHM) ||
                      eina_streq(engine, ELM_WAYLAND_EGL));

   if (sd->type == EFL_UI_WIN_TYPE_FAKE)
     sd->csd.need = EINA_FALSE;
   else if (eina_streq(s, "on"))
     sd->csd.need = EINA_TRUE;
   else if (eina_streq(s, "off"))
     sd->csd.need = EINA_FALSE;
   else
     sd->csd.need = sd->csd.wayland;
}

static void
_indicator_resized(void *data, const Efl_Event *event)
{
   ELM_WIN_DATA_GET_OR_RETURN(data, sd);
   Evas_Object *indicator = event->object;
   Evas_Coord_Size *size = (Evas_Coord_Size *)event->info;
   efl_gfx_hint_size_min_set(indicator, EINA_SIZE2D(size->w, size->h));
   _elm_win_frame_obj_update(sd, 0);
}

static Evas_Object*
_create_indicator(Evas_Object *obj)
{
   Evas_Object *indicator = NULL;
   const char *indicator_serv_name;

   indicator_serv_name = "elm_indicator_portrait";

   indicator = elm_plug_add(obj);
   if (!indicator)
     {
        ERR("Conformant cannot create plug to server[%s]", indicator_serv_name);
        return NULL;
     }

   if (!elm_plug_connect(indicator, indicator_serv_name, 0, EINA_FALSE))
     {
        ERR("Conformant cannot connect to server[%s]", indicator_serv_name);
        return NULL;
     }

   return indicator;
}

static void
_indicator_add(Efl_Ui_Win_Data *sd)
{
   Eo *obj = sd->obj;

   sd->indicator = _create_indicator(obj);

   if (!sd->indicator)
     return;

   if (elm_widget_is_legacy(obj))
     {
        if (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.indicator", sd->indicator))
          return;
     }
   else
     {
        if (!edje_object_part_swallow(sd->frame_obj, "efl.indicator", sd->indicator))
          return;
     }

   efl_event_callback_add
     (sd->indicator, ELM_PLUG_EVENT_IMAGE_RESIZED, _indicator_resized, obj);

   efl_canvas_object_is_frame_object_set(sd->indicator, EINA_TRUE);
   sd->csd.need_indicator = EINA_TRUE;

   _elm_win_frame_style_update(sd, 0, 1);
}

static void
_indicator_del(Efl_Ui_Win_Data *sd)
{
   Eo *obj = sd->obj;

   efl_event_callback_del
     (sd->indicator, ELM_PLUG_EVENT_IMAGE_RESIZED, _indicator_resized, obj);

   efl_del(sd->indicator);
   sd->indicator = NULL;

   sd->csd.need_indicator = EINA_FALSE;
   _elm_win_frame_style_update(sd, 0, 1);
}

static Eina_Value
_win_finalize_job_cb(void *data, const Eina_Value value)
{
   Efl_Ui_Win_Data *sd = data;
   sd->finalize_future = NULL;
   if (!efl_invalidated_get(sd->obj))
     evas_render_pending_objects_flush(sd->evas);
   return value;
}

static void
_gesture_manager_config_load(Eo *obj)
{
   Eina_Value val;
   Eo *gm = efl_provider_find(obj, EFL_CONFIG_INTERFACE);

   eina_value_setup(&val, EINA_VALUE_TYPE_DOUBLE);
   eina_value_set(&val, _elm_config->glayer_long_tap_start_timeout);
   efl_config_set(gm, "glayer_long_tap_start_timeout", &val);

   eina_value_set(&val, _elm_config->glayer_double_tap_timeout);
   efl_config_set(gm, "glayer_double_tap_timeout", &val);

   elm_config_scroll_thumbscroll_friction_set(_elm_config->thumbscroll_friction);
   elm_config_scroll_thumbscroll_momentum_threshold_set(_elm_config->thumbscroll_momentum_threshold);

   eina_value_set(&val, _elm_config->glayer_line_min_length);
   efl_config_set(gm, "glayer_line_min_length", &val);

   eina_value_set(&val, _elm_config->glayer_line_distance_tolerance);
   efl_config_set(gm, "glayer_line_distance_tolerance", &val);

   eina_value_set(&val, _elm_config->glayer_line_angular_tolerance);
   efl_config_set(gm, "glayer_line_angular_tolerance", &val);

   eina_value_set(&val, _elm_config->glayer_zoom_finger_factor);
   efl_config_set(gm, "glayer_zoom_finger_factor", &val);

   eina_value_set(&val, _elm_config->glayer_zoom_distance_tolerance);
   efl_config_set(gm, "glayer_zoom_distance_tolerance", &val);

   eina_value_setup(&val, EINA_VALUE_TYPE_UINT);
   eina_value_set(&val, _elm_config->glayer_flick_time_limit_ms);
   efl_config_set(gm, "glayer_flick_time_limit_ms", &val);

   eina_value_setup(&val, EINA_VALUE_TYPE_UCHAR);
   eina_value_set(&val, _elm_config->glayer_continues_enable);
   efl_config_set(gm, "glayer_continues_enable", &val);

   eina_value_set(&val, _elm_config->glayer_zoom_finger_enable);
   efl_config_set(gm, "glayer_zoom_finger_enable", &val);
}

static Eo *
_elm_win_finalize_internal(Eo *obj, Efl_Ui_Win_Data *sd, const char *name, Efl_Ui_Win_Type type)
{
   Evas_Object *parent = NULL;
   Evas *e;
   const Eina_List *l;
   const char *fontpath, *engine = NULL, *enginelist[32], *disp;
   int gl_depth = _elm_config->gl_depth;
   int gl_stencil = _elm_config->gl_stencil;
   int gl_msaa = _elm_config->gl_msaa;
   Eina_Stringshare *accel = NULL;
   Eina_Bool is_gl_accel;
   int i, p = 0;
   Ecore_Window parent_id = 0;

   Efl_Ui_Win_Data tmp_sd;

   if ((sd->type == ELM_WIN_INLINED_IMAGE) &&
       !efl_isa(obj, EFL_UI_WIN_INLINED_CLASS))
     {
        ERR("Failed to create an inlined window!");
        return NULL;
     }

   parent = efl_parent_get(obj);
   if (!(efl_isa(parent, EFL_UI_WIN_CLASS) ||
         efl_isa(parent, EFL_UI_WIN_LEGACY_CLASS) ||
         efl_isa(parent, EFL_UI_WIN_INLINED_CLASS))) parent = NULL;

   /* just to store some data while trying out to create a canvas */
   memset(&tmp_sd, 0, sizeof(Efl_Ui_Win_Data));

   is_gl_accel = _efl_ui_win_accel(sd, &accel, &gl_depth, &gl_stencil, &gl_msaa);

   switch ((int) type)
     {
      case EFL_UI_WIN_TYPE_FAKE:
        tmp_sd.ee = sd->ee;
        break;
      case ELM_WIN_INLINED_IMAGE:
        if (!parent) break;
          {
             Ecore_Evas *ee;

             e = evas_object_evas_get(parent);
             if (!e) break;

             ee = ecore_evas_ecore_evas_get(e);
             if (!ee) break;

             tmp_sd.img_obj = ecore_evas_object_image_new(ee);
             if (!tmp_sd.img_obj) break;

             tmp_sd.ee = ecore_evas_object_ecore_evas_get(tmp_sd.img_obj);
             if (!tmp_sd.ee)
               {
                  ELM_SAFE_FREE(tmp_sd.img_obj, evas_object_del);
               }
          }
        break;

      case ELM_WIN_SOCKET_IMAGE:
        tmp_sd.ee = ecore_evas_extn_socket_new(1, 1);
        break;

      default:
        disp = getenv("ELM_DISPLAY");
        if ((disp) && (!strcmp(disp, "buffer")))
          {
             enginelist[p++] = ELM_BUFFER;
          }
        else if ((disp) && (!strcmp(disp, "shot")))
          {
             enginelist[p++] = ENGINE_GET();
          }
// welcome to ifdef hell! :)

#ifdef HAVE_ELEMENTARY_X
        else if ((disp) && (!strcmp(disp, "x11")))
          {
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_OPENGL_X11;
                  enginelist[p++] = ELM_SOFTWARE_X11;
               }
             else
               {
                  enginelist[p++] = ELM_SOFTWARE_X11;
                  enginelist[p++] = ELM_OPENGL_X11;
               }
          }
#endif

#ifdef HAVE_ELEMENTARY_WL2
        else if ((disp) && (!strcmp(disp, "wl")))
          {
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_WAYLAND_EGL;
                  enginelist[p++] = ELM_WAYLAND_SHM;
               }
             else
               {
                  enginelist[p++] = ELM_WAYLAND_SHM;
                  enginelist[p++] = ELM_WAYLAND_EGL;
               }
          }
#endif

#ifdef HAVE_ELEMENTARY_WIN32
        else if ((disp) && (!strcmp(disp, "win")))
          {
             enginelist[p++] = ELM_SOFTWARE_WIN32;
             enginelist[p++] = ELM_OPENGL_WIN32;
             enginelist[p++] = ELM_SOFTWARE_DDRAW;
          }
#endif

#ifdef HAVE_ELEMENTARY_SDL
        else if ((disp) && (!strcmp(disp, "sdl")))
          {
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_OPENGL_SDL;
                  enginelist[p++] = ELM_SOFTWARE_SDL;
               }
             else
               {
                  enginelist[p++] = ELM_SOFTWARE_SDL;
                  enginelist[p++] = ELM_OPENGL_SDL;
               }
          }
#endif

#ifdef HAVE_ELEMENTARY_COCOA
        else if ((disp) && (!strcmp(disp, "mac")))
          {
             enginelist[p++] = ELM_OPENGL_COCOA;
          }
#endif

#if defined(HAVE_ELEMENTARY_DRM) || defined(HAVE_ELEMENTARY_FB)
        else if ((disp) && (!strcmp(disp, "fb")))
          {
#ifdef HAVE_ELEMENTARY_DRM
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_GL_DRM;
               }
             enginelist[p++] = ELM_DRM;
#endif
#ifdef HAVE_ELEMENTARY_FB
             enginelist[p++] = ELM_SOFTWARE_FB;
#endif
          }
#endif

#ifdef HAVE_ELEMENTARY_X
        else if (!_elm_preferred_engine &&
                 getenv("DISPLAY") && !getenv("ELM_ENGINE"))
          {
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_OPENGL_X11;
                  enginelist[p++] = ELM_SOFTWARE_X11;
               }
             else
               {
                  enginelist[p++] = ELM_SOFTWARE_X11;
                  enginelist[p++] = ELM_OPENGL_X11;
               }
          }
#endif
#ifdef HAVE_ELEMENTARY_WL2
        else if (!_elm_preferred_engine &&
                 getenv("WAYLAND_DISPLAY") && !getenv("ELM_ENGINE"))
          {
             if (is_gl_accel)
               {
                  enginelist[p++] = ELM_WAYLAND_EGL;
                  enginelist[p++] = ELM_WAYLAND_SHM;
               }
             else
               {
                  enginelist[p++] = ELM_WAYLAND_SHM;
                  enginelist[p++] = ELM_WAYLAND_EGL;
               }
          }
#endif
        else
          {
             if (is_gl_accel)
               {
// add all engines with selected engine first - if any
                  if (ENGINE_GET())
                    enginelist[p++] = ENGINE_GET();

// add all engines with gl/accelerated ones first - only engines compiled
#ifdef HAVE_ELEMENTARY_X
                  enginelist[p++] = ELM_OPENGL_X11;
#endif
#ifdef HAVE_ELEMENTARY_WL2
                  enginelist[p++] = ELM_WAYLAND_EGL;
#endif
#ifdef HAVE_ELEMENTARY_DRM
                  enginelist[p++] = ELM_GL_DRM;
                  enginelist[p++] = ELM_DRM;
#endif
#ifdef HAVE_ELEMENTARY_FB
                  enginelist[p++] = ELM_SOFTWARE_FB;
#endif
#ifdef HAVE_ELEMENTARY_COCOA
                  enginelist[p++] = ELM_OPENGL_COCOA;
#endif
#ifdef HAVE_ELEMENTARY_SDL
                  enginelist[p++] = ELM_OPENGL_SDL;
#endif
#ifdef HAVE_ELEMENTARY_X
                  enginelist[p++] = ELM_SOFTWARE_X11;
#endif
#ifdef HAVE_ELEMENTARY_WL2
                  enginelist[p++] = ELM_WAYLAND_SHM;
#endif
#ifdef HAVE_ELEMENTARY_WIN32
                  enginelist[p++] = ELM_SOFTWARE_WIN32;
                  enginelist[p++] = ELM_OPENGL_WIN32;
                  enginelist[p++] = ELM_SOFTWARE_DDRAW;
#endif
#ifdef HAVE_ELEMENTARY_SDL
                  enginelist[p++] = ELM_SOFTWARE_SDL;
#endif
               }
             else
               {
// add all engines with selected engine first - if any
                  if (elm_config_preferred_engine_get())
                    enginelist[p++] = elm_config_preferred_engine_get();
// add check _elm_gl_preference whether "none" or not
                  else if (_elm_config->engine &&
                           ((elm_config_accel_preference_get() &&
                             !strcmp(elm_config_accel_preference_get(), "none"))
                            || (accel && !strcmp(accel, "none"))))
                    enginelist[p++] = _elm_config->engine;
// add all engines with gl/accelerated ones first - only engines compiled
#ifdef HAVE_ELEMENTARY_X
                  enginelist[p++] = ELM_SOFTWARE_X11;
#endif
#ifdef HAVE_ELEMENTARY_WL2
                  enginelist[p++] = ELM_WAYLAND_SHM;
#endif
#ifdef HAVE_ELEMENTARY_DRM
                  enginelist[p++] = ELM_DRM;
#endif
#ifdef HAVE_ELEMENTARY_FB
                  enginelist[p++] = ELM_SOFTWARE_FB;
#endif
#ifdef HAVE_ELEMENTARY_COCOA
                  enginelist[p++] = ELM_OPENGL_COCOA;
#endif
#ifdef HAVE_ELEMENTARY_WIN32
                  enginelist[p++] = ELM_SOFTWARE_WIN32;
                  enginelist[p++] = ELM_OPENGL_WIN32;
                  enginelist[p++] = ELM_SOFTWARE_DDRAW;
#endif
#ifdef HAVE_ELEMENTARY_SDL
                  enginelist[p++] = ELM_SOFTWARE_SDL;
#endif
#ifdef HAVE_ELEMENTARY_X
                  enginelist[p++] = ELM_OPENGL_X11;
#endif
#ifdef HAVE_ELEMENTARY_WL2
                  enginelist[p++] = ELM_WAYLAND_EGL;
#endif
#ifdef HAVE_ELEMENTARY_DRM
                  enginelist[p++] = ELM_GL_DRM;
                  enginelist[p++] = ELM_DRM;
#endif
#ifdef HAVE_ELEMENTARY_SDL
                  enginelist[p++] = ELM_OPENGL_SDL;
#endif
               }
          }
        if (parent) parent_id = elm_win_window_id_get(parent);
        for (i = 0; i < p; i++)
          {
             int opt[20], opt_i = 0;

             if (_elm_config->vsync)
               {
                  opt[opt_i++] = ECORE_EVAS_OPT_VSYNC;
                  opt[opt_i++] = 1;
               }
             if (gl_depth)
               {
                  opt[opt_i++] = ECORE_EVAS_OPT_GL_DEPTH;
                  opt[opt_i++] = gl_depth;
               }
             if (gl_stencil)
               {
                  opt[opt_i++] = ECORE_EVAS_OPT_GL_STENCIL;
                  opt[opt_i++] = gl_stencil;
               }
             if (gl_msaa)
               {
                  opt[opt_i++] = ECORE_EVAS_OPT_GL_MSAA;
                  opt[opt_i++] = gl_msaa;
               }
             opt[opt_i] = 0;

             if (!strcmp(enginelist[i], ELM_SOFTWARE_X11))
               tmp_sd.ee = ecore_evas_software_x11_new(NULL, 0, 0, 0, 0, 0);
             else if (!strcmp(enginelist[i], ELM_OPENGL_X11))
               {
                  if (opt_i > 0)
                    tmp_sd.ee = ecore_evas_gl_x11_options_new(NULL, 0, 0, 0, 0, 0, opt);
                  else
                    tmp_sd.ee = ecore_evas_gl_x11_new(NULL, 0, 0, 0, 0, 0);
               }
             else if (!strcmp(enginelist[i], ELM_WAYLAND_SHM))
               tmp_sd.ee = _wayland_shm_new(NULL, parent_id, 0, 0, 0, 0, 0);
             else if (!strcmp(enginelist[i], ELM_WAYLAND_EGL))
               tmp_sd.ee = _wayland_egl_new(NULL, parent_id, 0, 0, 0, 0, 0, (opt_i > 0) ? opt : NULL);
             else if (!strcmp(enginelist[i], ELM_SOFTWARE_WIN32))
               tmp_sd.ee = ecore_evas_software_gdi_new(NULL, 0, 0, 1, 1);
             else if (!strcmp(enginelist[i], ELM_OPENGL_WIN32))
               tmp_sd.ee = ecore_evas_gl_win32_new(NULL, 1, 1, 0, 0);
             else if (!strcmp(enginelist[i], ELM_SOFTWARE_DDRAW))
               tmp_sd.ee = ecore_evas_software_ddraw_new(NULL, 0, 0, 1, 1);
             else if (!strcmp(enginelist[i], ELM_SOFTWARE_SDL))
               tmp_sd.ee = ecore_evas_sdl_new(NULL, 0, 0, 0, 0, 0, 1);
             else if (!strcmp(enginelist[i], ELM_OPENGL_SDL))
               tmp_sd.ee = ecore_evas_gl_sdl_new(NULL, 1, 1, 0, 0);
             else if (!strcmp(enginelist[i], ELM_OPENGL_COCOA))
               tmp_sd.ee = ecore_evas_cocoa_new(NULL, 1, 1, 0, 0);
             else if (!strcmp(enginelist[i], ELM_SOFTWARE_FB))
               tmp_sd.ee = ecore_evas_fb_new(NULL, 0, 1, 1);
             else if (!strcmp(enginelist[i], ELM_BUFFER))
               tmp_sd.ee = ecore_evas_buffer_new(1, 1);
             else if (!strcmp(enginelist[i], ELM_DRM))
               tmp_sd.ee = ecore_evas_drm_new(NULL, 0, 0, 0, 1, 1);
             else if (!strcmp(enginelist[i], ELM_GL_DRM))
               tmp_sd.ee = ecore_evas_gl_drm_new(NULL, 0, 0, 0, 1, 1);
             else if (!strncmp(enginelist[i], "shot:", 5))
               {
                  tmp_sd.ee = ecore_evas_buffer_new(1, 1);
                  ecore_evas_manual_render_set(tmp_sd.ee, EINA_TRUE);
                  tmp_sd.shot.info = eina_stringshare_add(ENGINE_GET() + 5);
               }
             engine = enginelist[i];
             if (tmp_sd.ee) break;
          }
        break;
     }

   eina_stringshare_del(accel);
   if (!tmp_sd.ee)
     {
        ERR("Cannot create window.");
        return NULL;
     }

   if (!sd->accel_pref)
     eina_stringshare_replace(&sd->accel_pref, elm_config_accel_preference_get());

   efl_parent_set(obj, ecore_evas_get(tmp_sd.ee));

   /* FIXME: Major hack: calling the constructor in the middle of finalize. */
   efl_constructor(efl_super(obj, MY_CLASS));
   evas_object_smart_callbacks_descriptions_set(obj, _smart_callbacks);

   if (getenv("ELM_FIRST_FRAME"))
     evas_event_callback_add(ecore_evas_get(tmp_sd.ee), EVAS_CALLBACK_RENDER_POST,
                             _elm_win_first_frame_do, getenv("ELM_FIRST_FRAME"));

   /* copying possibly altered fields back */
#define SD_CPY(_field)             \
  do                               \
    {                              \
       sd->_field = tmp_sd._field; \
    } while (0)

   SD_CPY(ee);
   SD_CPY(img_obj);
   SD_CPY(shot.info);
#undef SD_CPY

   if ((type != EFL_UI_WIN_TYPE_FAKE) && (trap) && (trap->add))
     sd->trap_data = trap->add(obj);

   /* complementary actions, which depend on final smart data
    * pointer */
   if (type == EFL_UI_WIN_TYPE_INLINED_IMAGE)
     _win_inlined_image_set(sd);
#ifdef HAVE_ELEMENTARY_X
   else if ((engine) &&
            ((!strcmp(engine, ELM_SOFTWARE_X11)) ||
             (!strcmp(engine, ELM_OPENGL_X11))))
     {
        sd->x.client_message_handler = ecore_event_handler_add
            (ECORE_X_EVENT_CLIENT_MESSAGE, _elm_win_client_message, obj);
        sd->x.property_handler = ecore_event_handler_add
            (ECORE_X_EVENT_WINDOW_PROPERTY, _elm_win_property_change, obj);
     }
#endif
   else if ((engine) && (!strncmp(engine, "shot:", 5)))
     _shot_init(sd);

   sd->kbdmode = EFL_UI_WIN_KEYBOARD_MODE_UNKNOWN;
   sd->legacy.indmode = ELM_WIN_INDICATOR_UNKNOWN;
   sd->indimode = EFL_UI_WIN_INDICATOR_MODE_OFF;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin) ecore_x_io_error_handler_set(_elm_x_io_err, NULL);
#endif

#ifdef HAVE_ELEMENTARY_WL2
   _elm_win_wlwindow_get(sd);
   if (sd->wl.win)
     {
        Ecore_Wl2_Window_Type wtype;
        sd->wl.configure_handler =
          ecore_event_handler_add(ECORE_WL2_EVENT_WINDOW_CONFIGURE, _elm_win_wl_configure, obj);
        switch (sd->type)
          {
           case ELM_WIN_BASIC:
           case ELM_WIN_DIALOG_BASIC:
           case ELM_WIN_NAVIFRAME_BASIC:
           case ELM_WIN_SPLASH:
           case ELM_WIN_TOOLBAR:
           case ELM_WIN_UTILITY:
           case ELM_WIN_DOCK:
           case ELM_WIN_DESKTOP:
             wtype = ECORE_WL2_WINDOW_TYPE_TOPLEVEL;
             break;
           case ELM_WIN_TOOLTIP:
           case ELM_WIN_COMBO:
           case ELM_WIN_MENU:
           case ELM_WIN_POPUP_MENU:
             wtype = ECORE_WL2_WINDOW_TYPE_MENU;
             break;
           case ELM_WIN_DND:
             wtype = ECORE_WL2_WINDOW_TYPE_DND;
             break;
           default:
             wtype = ECORE_WL2_WINDOW_TYPE_NONE;
          }
        ecore_wl2_window_type_set(sd->wl.win, wtype);
     }
   else if (sd->type == EFL_UI_WIN_TYPE_FAKE)
     {
        const char *env = getenv("WAYLAND_DISPLAY");
        if (env)
          {
             Ecore_Wl2_Display *d = ecore_wl2_display_connect(env);
             sd->wl.win = ecore_wl2_window_new(d, NULL, 0, 0, 1, 1);
             ecore_wl2_display_disconnect(d);
          }
     }
#endif

#ifdef HAVE_ELEMENTARY_COCOA
   _elm_win_cocoawindow_get(sd);
#endif
#ifdef HAVE_ELEMENTARY_WIN32
   sd->win32.key_down_handler = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN,
                                                        _elm_win_key_down, obj);
   _internal_elm_win_win32window_get(sd);
#endif

   if ((_elm_config->bgpixmap)
#ifdef HAVE_ELEMENTARY_X
       && (((sd->x.xwin) && (!ecore_x_screen_is_composited(0))) ||
           (!sd->x.xwin))
#endif
      )
     TRAP(sd, avoid_damage_set, ECORE_EVAS_AVOID_DAMAGE_EXPOSE);
   // bg pixmap done by x - has other issues like can be redrawn by x before it
   // is filled/ready by app
   //     TRAP(sd, avoid_damage_set, ECORE_EVAS_AVOID_DAMAGE_BUILT_IN);

   sd->type = type;
   sd->parent = parent;
   sd->modal_count = 0;
   sd->withdrawn = ecore_evas_withdrawn_get(sd->ee);

   if (sd->parent)
     evas_object_event_callback_add
       (sd->parent, EVAS_CALLBACK_DEL, _elm_win_on_parent_del, obj);

   sd->evas = ecore_evas_get(sd->ee);

   evas_object_color_set(obj, 0, 0, 0, 0);
   evas_object_pass_events_set(obj, EINA_TRUE);

   if (type == EFL_UI_WIN_TYPE_INLINED_IMAGE)
     efl_ui_win_inlined_parent_set(obj, parent);

   /* use own version of ecore_evas_object_associate() that does TRAP() */
   ecore_evas_data_set(sd->ee, "elm_win", obj);

   if (type != EFL_UI_WIN_TYPE_FAKE)
     {
        evas_object_event_callback_add(obj, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
           _elm_win_obj_callback_changed_size_hints, NULL);
        evas_object_intercept_raise_callback_add
          (obj, _elm_win_obj_intercept_raise, obj);
        evas_object_intercept_lower_callback_add
          (obj, _elm_win_obj_intercept_lower, obj);
        evas_object_intercept_stack_above_callback_add
          (obj, _elm_win_obj_intercept_stack_above, obj);
        evas_object_intercept_stack_below_callback_add
          (obj, _elm_win_obj_intercept_stack_below, obj);
        evas_object_intercept_layer_set_callback_add
          (obj, _elm_win_obj_intercept_layer_set, obj);
     }

   TRAP(sd, name_class_set, name, _elm_appname);
   TRAP(sd, title_set, sd->title);
   ecore_evas_callback_delete_request_set(sd->ee, _elm_win_delete_request);
   ecore_evas_callback_state_change_set(sd->ee, _elm_win_state_change);
   ecore_evas_callback_focus_in_set(sd->ee, _elm_win_focus_in);
   ecore_evas_callback_focus_out_set(sd->ee, _elm_win_focus_out);
   ecore_evas_callback_resize_set(sd->ee, _elm_win_resize);
   ecore_evas_callback_move_set(sd->ee, _elm_win_move);
   ecore_evas_callback_pre_render_set(sd->ee, _elm_win_pre_render);
   if (type != EFL_UI_WIN_TYPE_FAKE)
     {
         ecore_evas_callback_mouse_in_set(sd->ee, _elm_win_mouse_in);
         ecore_evas_callback_mouse_out_set(sd->ee, _elm_win_mouse_out);
     }

   evas_object_event_callback_add(obj, EVAS_CALLBACK_HIDE, _elm_win_cb_hide, NULL);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _elm_win_cb_show, NULL);

   evas_image_cache_set(sd->evas, (_elm_config->image_cache * 1024));
   evas_font_cache_set(sd->evas, (_elm_config->font_cache * 1024));

   EINA_LIST_FOREACH(_elm_config->font_dirs, l, fontpath)
     evas_font_path_append(sd->evas, fontpath);

   if (!_elm_config->font_hinting)
     evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_NONE);
   else if (_elm_config->font_hinting == 1)
     evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_AUTO);
   else if (_elm_config->font_hinting == 2)
     evas_font_hinting_set(sd->evas, EVAS_FONT_HINTING_BYTECODE);

   sd->wm_rot.wm_supported = ecore_evas_wm_rotation_supported_get(sd->ee);
   sd->wm_rot.preferred_rot = -1; // it means that elm_win doesn't use preferred rotation.

#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif

   if (type != EFL_UI_WIN_TYPE_FAKE)
     {
        //Prohibiting auto-rendering, until elm_win is shown.
        if (_elm_win_auto_norender_withdrawn(obj))
          {
             if (elm_win_withdrawn_get(obj))
               {
                  if (!evas_object_data_get(obj, "__win_auto_norender"))
                    {
                       elm_win_norender_push(obj);
                       evas_object_data_set(obj, "__win_auto_norender", obj);
                    }
               }
          }
     }

#ifdef ELM_DEBUG
   Evas_Modifier_Mask mask = evas_key_modifier_mask_get(sd->evas, "Control");
   evas_object_event_callback_add
     (obj, EVAS_CALLBACK_KEY_DOWN, _debug_key_down, NULL);

   if (evas_object_key_grab(obj, "F12", mask, 0, EINA_TRUE))
     INF("Ctrl+F12 key combination exclusive for dot tree generation\n");
   else
     ERR("failed to grab F12 key to elm widgets (dot) tree generation");
#endif

   if (type != EFL_UI_WIN_TYPE_FAKE)
     {
        if ((_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_ON) ||
            ((_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_AUTO) &&
             ((engine) &&
              ((!strcmp(engine, ELM_SOFTWARE_FB)) || (!strcmp(engine, ELM_DRM))
              || (!strcmp(engine, ELM_GL_DRM))))))
          {
             Evas_Object *o;
             Evas_Coord mw = 1, mh = 1, hx = 0, hy = 0;

             sd->pointer.obj = o = edje_object_add(ecore_evas_get(tmp_sd.ee));
             elm_widget_theme_object_set(obj, o, "pointer", "base", "default");
             edje_object_size_min_calc(o, &mw, &mh);
             evas_object_resize(o, mw, mh);
             if (elm_widget_is_legacy(obj))
               edje_object_part_geometry_get(o, "elm.swallow.hotspot",
                                             &hx, &hy, NULL, NULL);
             else
               edje_object_part_geometry_get(o, "efl.hotspot",
                                             &hx, &hy, NULL, NULL);

             sd->pointer.hot_x = hx;
             sd->pointer.hot_y = hy;
             evas_object_show(o);
             ecore_evas_object_cursor_set(tmp_sd.ee, o, EVAS_LAYER_MAX, hx, hy);
          }
        else if (_elm_config->softcursor_mode == ELM_SOFTCURSOR_MODE_OFF)
          {
             // do nothing
          }
     }

   _elm_win_legacy_init(sd);
   _elm_win_need_frame_adjust(sd, engine);
   _elm_win_apply_alpha(obj, sd);

#ifdef HAVE_ELEMENTARY_WL2
   if ((type != EFL_UI_WIN_TYPE_FAKE) && (type != EFL_UI_WIN_TYPE_INLINED_IMAGE))
     {
        if ((engine) &&
            ((!strcmp(engine, ELM_WAYLAND_SHM) ||
              (!strcmp(engine, ELM_WAYLAND_EGL)))))
          {
             Evas *pevas;

             if (!strcmp(engine, ELM_WAYLAND_SHM))
               sd->pointer.ee = ecore_evas_wayland_shm_new(NULL, 0, 0, 0, 0, 0, 0);
             else if (!strcmp(engine, ELM_WAYLAND_EGL))
               sd->pointer.ee = ecore_evas_wayland_egl_new(NULL, 0, 0, 0, 0, 0, 0);
             ecore_evas_alpha_set(sd->pointer.ee, EINA_TRUE);

             pevas = ecore_evas_get(sd->pointer.ee);

             sd->pointer.obj = edje_object_add(pevas);

             sd->pointer.win = ecore_evas_wayland2_window_get(sd->pointer.ee);
             ecore_wl2_window_type_set(sd->pointer.win,
                                       ECORE_WL2_WINDOW_TYPE_NONE);
             _elm_win_wl_cursor_set(sd->obj, NULL);
             ecore_evas_show(sd->pointer.ee);
          }
     }
#endif

   /* do not append to list; all windows render as black rects */
   if (type != EFL_UI_WIN_TYPE_FAKE)
     {
        const char *element = "base";
        const char *style;

        _elm_win_list = eina_list_append(_elm_win_list, obj);
        _elm_win_count++;

        if ((engine) &&
            ((!strcmp(engine, ELM_SOFTWARE_FB)) || (!strcmp(engine, ELM_DRM))
             || (!strcmp(engine, ELM_GL_DRM))))
          {
             TRAP(sd, fullscreen_set, 1);
          }
        style = elm_widget_style_get(obj);
        if (!style) style = "default";
        switch (type)
          {
           case EFL_UI_WIN_TYPE_DIALOG_BASIC:    element = "dialog"; break;
           case EFL_UI_WIN_TYPE_NAVIFRAME_BASIC: element = "naviframe"; break;
           default: break;
          }

        if (!_elm_config->win_no_border)
          _elm_win_frame_add(sd, element, style);

        if (sd->indimode != EFL_UI_WIN_INDICATOR_MODE_OFF)
          _indicator_add(sd);

        if (_elm_config->focus_highlight_enable)
          elm_win_focus_highlight_enabled_set(obj, EINA_TRUE);
        if (_elm_config->focus_highlight_animate)
          elm_win_focus_highlight_animate_set(obj, EINA_TRUE);
     }

   efl_access_object_role_set(obj, EFL_ACCESS_ROLE_WINDOW);
   if (_elm_config->atspi_mode)
     efl_access_window_created_signal_emit(obj);

   // attach config API
   efl_composite_attach(obj, efl_provider_find(efl_main_loop_get(), EFL_CONFIG_GLOBAL_CLASS));

   efl_event_callback_array_add(sd->evas, _elm_evas_tracking(), sd);
   efl_event_callback_array_add(obj, _elm_win_evas_feed_fake_callbacks(), sd->evas);
   efl_event_callback_array_add(obj, _elm_win_tracking(), sd);
   evas_object_show(sd->legacy.edje);

   if (type == EFL_UI_WIN_TYPE_FAKE)
     {
        _elm_win_resize_job(obj);
        _elm_win_move(sd->ee);
     }
   else
     {
        sd->finalize_future = eina_future_then_easy(efl_loop_job(efl_loop_get(obj)),
                              .success = _win_finalize_job_cb, .data = sd);
     }

   // All normal windows are "standard" windows with EO API
   if (!sd->legacy.ctor)
     {
        switch (type)
          {
           case EFL_UI_WIN_TYPE_UNKNOWN:
           case EFL_UI_WIN_TYPE_BASIC:
           case EFL_UI_WIN_TYPE_DIALOG_BASIC:
             _elm_win_standard_init(obj);
             break;
           default: break;
          }
     }

   // Load the config values into gesutre manager.
   _gesture_manager_config_load(obj);

   return obj;
}

EOLIAN static Eo *
_efl_ui_win_efl_object_finalize(Eo *obj, Efl_Ui_Win_Data *sd)
{
   Eina_Bool resume = !_elm_win_count;

   obj = _elm_win_finalize_internal(obj, sd, sd->name, sd->type);
   if (!obj) return NULL;
   obj = efl_finalize(efl_super(obj, MY_CLASS));
   if (obj && resume) efl_event_callback_call(efl_loop_get(obj), EFL_APP_EVENT_RESUME, NULL);
   if (obj && (!elm_widget_is_legacy(obj)))
     {
        /* FIXME: if parts other than background are supported then this should change */
        if (efl_file_get(efl_super(efl_part(obj, "background"), EFL_UI_WIN_PART_CLASS)) ||
            efl_file_mmap_get(efl_super(efl_part(obj, "background"), EFL_UI_WIN_PART_CLASS)))
          efl_file_load(efl_part(obj, "background"));
     }
   _ee_backbone_init(obj, sd);
   return obj;
}

EOLIAN static void
_efl_ui_win_efl_canvas_object_legacy_ctor(Eo *obj, Efl_Ui_Win_Data *sd)
{
   efl_canvas_object_legacy_ctor(efl_super(obj, MY_CLASS));
   sd->legacy.ctor = EINA_TRUE;
}

EOLIAN static Efl_Ui_Focus_Manager*
_efl_ui_win_efl_ui_widget_focus_manager_focus_manager_create(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd EINA_UNUSED, Efl_Ui_Focus_Object *root)
{
   Efl_Ui_Focus_Manager *manager;

   manager = efl_add(EFL_UI_FOCUS_MANAGER_CALC_CLASS, obj,
     efl_ui_focus_manager_root_set(efl_added, root)
   );

   return manager;
}

EOLIAN static void
_efl_ui_win_efl_object_destructor(Eo *obj, Efl_Ui_Win_Data *pd EINA_UNUSED)
{
#ifdef HAVE_ELEMENTARY_WL2
   if (pd->type == EFL_UI_WIN_TYPE_FAKE)
     {
        if (pd->wl.win)
          ecore_wl2_window_free(pd->wl.win);
     }
#endif
   if (pd->finalize_future)
     eina_future_cancel(pd->finalize_future);

   _ee_backbone_shutdown(obj, pd);

   efl_destructor(efl_super(obj, MY_CLASS));

   efl_unref(pd->provider);
}

EOLIAN static Eo *
_efl_ui_win_efl_object_constructor(Eo *obj, Efl_Ui_Win_Data *pd)
{
   /* UGLY HACK: Do (almost) nothing here:
    * We are calling the constructor chain from the finalizer. It's
    * really bad and hacky. Needs fixing. */

   pd->obj = obj;
   pd->provider = efl_add_ref(EFL_UI_FOCUS_PARENT_PROVIDER_STANDARD_CLASS, NULL);
   pd->profile.available = eina_array_new(4);
   pd->max_w = pd->max_h = -1;
   pd->planned_changes = eina_array_new(10);

   // For bindings: if no parent, allow simple unref
   if (!efl_parent_get(obj))
     efl_allow_parent_unref_set(obj, EINA_TRUE);

   if (!elm_widget_is_legacy(obj))
     pd->type = EFL_UI_WIN_TYPE_BASIC;

   return obj;
}

EOLIAN static void
_efl_ui_win_efl_text_text_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *title)
{
   if (!title) return;
   eina_stringshare_replace(&(sd->title), title);
   if (sd->ee)
     TRAP(sd, title_set, sd->title);
   if (sd->frame_obj)
     {
        if (elm_widget_is_legacy(sd->obj))
          edje_object_part_text_escaped_set(sd->frame_obj, "elm.text.title", sd->title);
        else
          edje_object_part_text_escaped_set(sd->frame_obj, "efl.text.title", sd->title);
     }
}

EOLIAN static const char*
_efl_ui_win_efl_text_text_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->title;
}

EOLIAN void
_efl_ui_win_efl_ui_i18n_language_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *locale)
{
   if (sd->frame_obj)
     efl_ui_language_set(sd->frame_obj, locale);
}

EOLIAN const char *
_efl_ui_win_efl_ui_i18n_language_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->frame_obj ? efl_ui_language_get(sd->frame_obj) : NULL;
}

EOLIAN static void
_efl_ui_win_efl_ui_l10n_l10n_text_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *label, const char *domain)
{
   if (sd->frame_obj)
     efl_ui_l10n_text_set(efl_part(obj, "efl.text.title"), label, domain);
}

EOLIAN static const char *
_efl_ui_win_efl_ui_l10n_l10n_text_get(const Eo *obj, Efl_Ui_Win_Data *sd, const char **domain)
{
  return sd->frame_obj ?
    efl_ui_l10n_text_get(efl_part(obj, "efl.text.title"), domain) : NULL;
}

EOLIAN static void
_efl_ui_win_win_type_set(Eo *obj, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Type type)
{
   if (efl_finalized_get(obj))
     {
        ERR("This function is only allowed during construction.");
        return;
     }
   sd->type = type;
}

EOLIAN static Efl_Ui_Win_Type
_efl_ui_win_win_type_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->type;
}

EOLIAN static void
_efl_ui_win_win_name_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *name)
{
   if (efl_finalized_get(obj))
     {
        ERR("This function is only allowed during construction.");
        return;
     }
   sd->name = eina_stringshare_add(name);
}

EOLIAN static const char *
_efl_ui_win_win_name_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->name;
}

EOLIAN static void
_efl_ui_win_accel_preference_set(Eo *obj, Efl_Ui_Win_Data *pd, const char *accel)
{
   if (efl_finalized_get(obj))
     {
        ERR("This function is only allowed during construction.");
        return;
     }
   eina_stringshare_replace(&pd->accel_pref, accel);
}

EOLIAN static const char *
_efl_ui_win_accel_preference_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd)
{
   return pd->accel_pref;
}

EOLIAN static void
_efl_ui_win_win_role_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *role)
{
   if (!role) return;
   eina_stringshare_replace(&(sd->role), role);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static const char*
_efl_ui_win_win_role_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->role;
}

EOLIAN static void
_efl_ui_win_icon_object_set(Eo *obj, Efl_Ui_Win_Data *sd, Evas_Object *icon)
{
   if (icon && (!efl_isa(icon, EFL_CANVAS_IMAGE_INTERNAL_CLASS) &&
                !efl_isa(icon, EFL_UI_IMAGE_CLASS)))
     {
        ERR("Icon object type is not supported!");
        efl_del(icon);
        return;
     }

   if (sd->icon)
     {
        efl_event_callback_del(sd->icon, EFL_EVENT_DEL, _elm_win_on_icon_del, obj);
        efl_del(sd->icon);
     }
   sd->icon = icon;
   if (sd->icon)
     {
        efl_event_callback_add(sd->icon, EFL_EVENT_DEL, _elm_win_on_icon_del, obj);
        if (sd->frame_obj)
          {
             if (elm_widget_is_legacy(sd->obj))
               edje_object_part_swallow(sd->frame_obj, "elm.swallow.icon", sd->icon);
             else
               edje_object_part_swallow(sd->frame_obj, "efl.icon", sd->icon);

             evas_object_is_frame_object_set(sd->icon, EINA_TRUE);
          }
     }
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static const Evas_Object*
_efl_ui_win_icon_object_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->icon;
}

EOLIAN static const Eina_Value *
_efl_ui_win_exit_on_close_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return &sd->exit_on_close;
}

EOLIAN static void
_efl_ui_win_exit_on_close_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const Eina_Value *exit_code)
{
   const Eina_Value_Type *type = eina_value_type_get(exit_code);

   if (type)
     eina_value_copy(exit_code, &sd->exit_on_close);
   else
     eina_value_flush(&sd->exit_on_close);
}

/* Only for C API */
EAPI void
elm_win_autodel_set(Eo *obj, Eina_Bool autodel)
{
   ELM_WIN_DATA_GET_OR_RETURN(obj, sd);
   sd->autodel = autodel;
}

EAPI Eina_Bool
elm_win_autodel_get(const Eo *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;
   return sd->autodel;
}

EOLIAN static void
_efl_ui_win_autohide_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool autohide)
{
   sd->autohide = autohide;
}

EOLIAN static Eina_Bool
_efl_ui_win_autohide_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->autohide;
}

EOLIAN static void
_efl_ui_win_activate(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   TRAP(sd, activate);
}

EOLIAN static void
_efl_ui_win_efl_gfx_stack_raise_to_top(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd)
{
   TRAP(pd, raise);
}

EAPI void
elm_win_raise(Eo *obj)
{
   efl_gfx_stack_raise_to_top(obj);
}

EOLIAN static void
_efl_ui_win_efl_gfx_stack_lower_to_bottom(Eo *obj, Efl_Ui_Win_Data *pd EINA_UNUSED)
{
   // Do nothing: in X we could request to stack lower but that has been abused
   // and transformed into a kind of "iconify". As a consequence, lower is
   // not allowed in EO land.
   if (!elm_widget_is_legacy(obj)) return;

   // Legacy support...
   elm_win_lower(obj);
}

EOLIAN static void
_efl_ui_win_center(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool h, Eina_Bool v)
{
   int win_w, win_h, screen_x, screen_y, screen_w, screen_h, nx, ny;

   if (sd->deferred_resize_job) _elm_win_resize_job(sd->obj);
   if (sd->frame_obj) edje_object_message_signal_process(sd->frame_obj);
   evas_smart_objects_calculate(evas_object_evas_get(obj));
   _elm_win_resize_objects_eval(obj, EINA_FALSE);
   if ((trap) && (trap->center) && (!trap->center(sd->trap_data, obj, h, v)))
     return;

   if (!efl_gfx_entity_visible_get(obj))
     {
        // Chose to use env var so this will also translate more easily
        // to wayland. yes - we can get x atoms to figure out if wm is
        // enlightenment, but this works just as well. for wl we'd need
        // an alternate wl specific way... this below works better IMHO
        const char *s = getenv("DESKTOP");

        if ((s) && (!strcasecmp(s, "Enlightenment")))
          {
#ifdef HAVE_ELEMENTARY_X
             if (sd->x.xwin)
               {
                  static Ecore_X_Atom state = 0;
                  static Ecore_X_Atom centered = 0;

                  _internal_elm_win_xwindow_get(sd);
                  if (!centered) centered = ecore_x_atom_get
                    ("__E_ATOM_WINDOW_STATE_CENTERED");
                  if (!state) state = ecore_x_atom_get
                    ("__E_ATOM_WINDOW_STATE");
                  ecore_x_window_prop_card32_set(sd->x.xwin, state, &centered, 1);
               }
#endif
// XXX: what to do with wayland?
// XXX: what to do with win32?
// XXX: what to do with osx/coca?
// etc.
             return;
          }
        // not e - fall back to manually placing on what we think the screen
        // is/will be... to do this move window to where pointer is first
#ifdef HAVE_ELEMENTARY_X
        if (sd->x.xwin)
          {
             int x = 0, y = 0;

             if (sd->req_wh)
               {
                  win_w = sd->req_w;
                  win_h = sd->req_h;
               }
             else evas_object_geometry_get(obj, NULL, NULL, &win_w, &win_h);
             ecore_x_pointer_root_xy_get(&x, &y);
             ecore_evas_move(sd->ee, x - (win_w / 2), y - (win_h / 2));
          }
#endif
// XXX: what to do with wayland?
// XXX: what to do with win32?
// XXX: what to do with osx/coca?
// etc.
     }

   ecore_evas_screen_geometry_get(sd->ee,
                                  &screen_x, &screen_y,
                                  &screen_w, &screen_h);
   if ((!screen_w) || (!screen_h)) return;

   if (sd->req_wh)
     {
        win_w = sd->req_w;
        win_h = sd->req_h;
     }
   else evas_object_geometry_get(obj, NULL, NULL, &win_w, &win_h);
   if (sd->req_xy)
     {
        nx = sd->req_x;
        ny = sd->req_y;
     }
   else evas_object_geometry_get(obj, &nx, &ny, NULL, NULL);

   if ((!win_w) || (!win_h)) return;

   if (h) nx = win_w >= screen_w ? 0 : (screen_w / 2) - (win_w / 2);
   if (v) ny = win_h >= screen_h ? 0 : (screen_h / 2) - (win_h / 2);

   sd->req_xy = EINA_TRUE;
   sd->req_x = screen_x + nx;
   sd->req_y = screen_y + ny;
   evas_object_move(obj, screen_x + nx, screen_y + ny);
}

EOLIAN static void
_efl_ui_win_borderless_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool borderless)
{
   sd->csd.need_borderless = borderless ? 1 : 0;
   _elm_win_frame_style_update(sd, 0, 1);

#ifdef HAVE_ELEMENTARY_X
   if (!sd->x.xwin || !sd->csd.need)
#endif
     TRAP(sd, borderless_set, borderless);

   _elm_win_resize_objects_eval(obj, EINA_FALSE);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Eina_Bool
_efl_ui_win_borderless_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (!sd->csd.need)
     return ecore_evas_borderless_get(sd->ee);
   return sd->csd.need_borderless;
}

EOLIAN static void
_efl_ui_win_alpha_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Bool enabled)
{
   sd->application_alpha = enabled;
   _elm_win_apply_alpha(obj, sd);
   _elm_win_frame_style_update(sd, 0, 1);
}

EOLIAN static Eina_Bool
_efl_ui_win_alpha_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (sd->img_obj)
     {
        return evas_object_image_alpha_get(sd->img_obj);
     }
   else
     {
        return ecore_evas_alpha_get(sd->ee);
     }

   return EINA_FALSE;
}

EOLIAN static void
_efl_ui_win_fullscreen_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool fullscreen)
{
   const char *engine_name = ecore_evas_engine_name_get(sd->ee);
   // YYY: handle if sd->img_obj
   if (engine_name &&
       ((!strcmp(engine_name, ELM_SOFTWARE_FB)) ||
        (!strcmp(engine_name, ELM_DRM)) ||
        (!strcmp(engine_name, ELM_GL_DRM))))
     {
        // these engines... can ONLY be fullscreen
        return;
     }
   else if (sd->type == EFL_UI_WIN_TYPE_FAKE)
     sd->fullscreen = !!fullscreen;
   else
     {
//        sd->fullscreen = fullscreen;
        TRAP(sd, fullscreen_set, fullscreen);

#ifdef HAVE_ELEMENTARY_X
        _elm_win_xwin_update(sd);
#endif
     }
}

EOLIAN static Eina_Bool
_efl_ui_win_fullscreen_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   const char *engine_name = ecore_evas_engine_name_get(sd->ee);

   if (engine_name &&
       ((!strcmp(engine_name, ELM_SOFTWARE_FB)) ||
        (!strcmp(engine_name, ELM_DRM)) ||
        (!strcmp(engine_name, ELM_GL_DRM))))
     {
        // these engines... can ONLY be fullscreen
        return EINA_TRUE;
     }
   return sd->fullscreen;
}

static inline Eo *
_main_menu_swallow_get(Efl_Ui_Win_Data *sd)
{
   Eina_Bool legacy_menu_swallow = EINA_TRUE;
   const char *data;
   int version;

   data = edje_object_data_get(sd->legacy.edje, "version");
   version = data ? atoi(data) : 0;
   if (version >= 119) legacy_menu_swallow = EINA_FALSE;

#ifdef HAVE_ELEMENTARY_COCOA
   if (sd->cocoa.win) legacy_menu_swallow = EINA_TRUE;
#endif

   if (legacy_menu_swallow)
     {
        DBG("Detected legacy theme, using legacy swallows.");
        return sd->legacy.edje;
     }
   return sd->frame_obj;
}

static void
_main_menu_resize_cb(void *data EINA_UNUSED, const Efl_Event *ev)
{
   // After resize, the framespace size has changed, so update the win geometry
   _elm_win_resize_objects_eval(ev->object, EINA_FALSE);
   efl_event_callback_del(ev->object, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _main_menu_resize_cb, NULL);
}

static void
_dbus_menu_set(Eina_Bool dbus_connect, void *data)
{
   ELM_WIN_DATA_GET_OR_RETURN(data, sd);
   Eo *swallow = _main_menu_swallow_get(sd);

   if (dbus_connect)
     {
        DBG("Setting menu to D-Bus");
        edje_object_part_unswallow(swallow, sd->main_menu);
        sd->csd.need_menu = EINA_FALSE;
        _elm_menu_menu_bar_hide(sd->main_menu);
        _elm_win_resize_objects_eval(sd->obj, EINA_FALSE);
        if (swallow != sd->frame_obj)
          {
             // Note: Based on EFL 1.18 the signal was "elm,action,hide"
             // and not "elm,action,hide_menu" as expected.
             if (elm_widget_is_legacy(data))
               edje_object_signal_emit(swallow, "elm,action,hide", "elm");
             else
               edje_object_signal_emit(swallow, "efl,action,hide", "efl");
             edje_object_message_signal_recursive_process(swallow);
          }
     }
   else
     {
        DBG("Setting menu to local mode");
        efl_event_callback_add(sd->obj, EFL_GFX_ENTITY_EVENT_SIZE_CHANGED, _main_menu_resize_cb, NULL);

        if (elm_widget_is_legacy(sd->obj))
          edje_object_part_swallow(swallow, "elm.swallow.menu", sd->main_menu);
        else
          edje_object_part_swallow(swallow, "efl.menu", sd->main_menu);

        evas_object_show(sd->main_menu);
        if (swallow == sd->frame_obj)
          {
             efl_canvas_object_is_frame_object_set(sd->main_menu, EINA_TRUE);
             sd->csd.need_menu = EINA_TRUE;
          }
        else
          {
             if (elm_widget_is_legacy(data))
               edje_object_signal_emit(swallow, "elm,action,show_menu", "elm");
             else
               edje_object_signal_emit(swallow, "efl,action,show_menu", "efl");
             edje_object_message_signal_recursive_process(swallow);
          }
     }
   _elm_win_frame_style_update(sd, 0, 1);
   //sd->deferred_resize_job = EINA_TRUE;
}

EOLIAN static const Eina_Value *
_efl_ui_win_exit_on_all_windows_closed_get(void)
{
   return &exit_on_all_windows_closed;
}

EOLIAN static void
_efl_ui_win_exit_on_all_windows_closed_set(const Eina_Value *exit_code)
{
   const Eina_Value_Type *type = eina_value_type_get(exit_code);

   if (type)
     eina_value_copy(exit_code, &exit_on_all_windows_closed);
   else
     eina_value_flush(&exit_on_all_windows_closed);
}

EOLIAN static void
_efl_ui_win_maximized_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool maximized)
{
   _elm_win_frame_style_update(sd, 0, 1);
   // YYY: handle if sd->img_obj
   TRAP(sd, maximized_set, maximized);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Eina_Bool
_efl_ui_win_maximized_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->maximized;
}

EOLIAN static void
_efl_ui_win_minimized_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool minimized)
{
//   sd->minimized = minimized;
   TRAP(sd, iconified_set, minimized);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Eina_Bool
_efl_ui_win_minimized_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->minimized;
}

EOLIAN static void
_efl_ui_win_wm_available_profiles_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const Eina_Array *profiles)
{
   Eina_Bool found = EINA_FALSE;

   _elm_win_available_profiles_del(sd);
   if (profiles && eina_array_count(profiles))
     {
        Eina_Iterator *it;
        const char *prof;

        it = eina_array_iterator_new(profiles);
        EINA_ITERATOR_FOREACH(it, prof)
          {
             Eina_Stringshare *str = eina_stringshare_add(prof);
             if (!str) continue;

             eina_array_push(sd->profile.available, str);
             /* check to see if a given array has a current profile of elm_win */
             if (str == sd->profile.name)
               found = EINA_TRUE;
          }
        eina_iterator_free(it);
     }

   if (ecore_evas_window_profile_supported_get(sd->ee))
     {
        ecore_evas_window_available_profiles_set(sd->ee,
                                                 (const char **) sd->profile.available->data,
                                                 eina_array_count(sd->profile.available));

        /* current profile of elm_win is wrong, change profile */
        if (!found && eina_array_count(sd->profile.available))
          {
             eina_stringshare_replace(&(sd->profile.name),
                                      eina_array_data_get(sd->profile.available, 0));
             ecore_evas_window_profile_set(sd->ee, sd->profile.name);
          }

     }
   else
     {
        if (eina_array_count(sd->profile.available))
          _elm_win_profile_update(sd);
     }
}

EOLIAN static const Eina_Array *
_efl_ui_win_wm_available_profiles_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (ecore_evas_window_profile_supported_get(sd->ee))
     {
        char **profiles = NULL; // all const
        unsigned int count, i;
        Eina_Bool ok;

        ok = ecore_evas_window_available_profiles_get(sd->ee, &profiles, &count);
        if (!ok) return NULL;

        if (count == eina_array_count(sd->profile.available))
          {
             for (i = 0; ok && (i < count); i++)
               {
                  if (!eina_streq(profiles[i], eina_array_data_get(sd->profile.available, i)))
                    ok = EINA_FALSE;
               }
             if (ok) return sd->profile.available;
          }

        // Oops! What is going on here? Can this happen?
        INF("Available profile list has changed in ecore evas!");
        _elm_win_available_profiles_del(sd);
        for (i = 0; i < count; i++)
          {
             Eina_Stringshare *str = eina_stringshare_add(profiles[i]);
             if (str) eina_array_push(sd->profile.available, str);
          }
     }

   return sd->profile.available;
}

EOLIAN static void
_efl_ui_win_urgent_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Urgent_Mode urgent)
{
   Eina_Bool urgent_tmp = !!urgent;

   if (sd->urgent == urgent_tmp) return;

   sd->urgent = urgent_tmp;
   TRAP(sd, urgent_set, urgent_tmp);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Efl_Ui_Win_Urgent_Mode
_efl_ui_win_urgent_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (sd->urgent) return EFL_UI_WIN_URGENT_MODE_URGENT;
   return EFL_UI_WIN_URGENT_MODE_NONE;
}

EOLIAN static void
_efl_ui_win_modal_set(Eo *obj, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Modal_Mode modal)
{
   Eina_Bool modal_tmp = !!modal;

   if (sd->modal_count) return;

   if ((modal_tmp) && (!sd->modal) && (evas_object_visible_get(obj)))
     _elm_win_modality_increment(sd);
   else if ((!modal_tmp) && (sd->modal) && (evas_object_visible_get(obj)))
     _elm_win_modality_decrement(sd);

   sd->modal = modal_tmp;
   TRAP(sd, modal_set, modal_tmp);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Efl_Ui_Win_Modal_Mode
_efl_ui_win_modal_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (sd->modal) return EFL_UI_WIN_MODAL_MODE_MODAL;
   return EFL_UI_WIN_MODAL_MODE_NONE;
}

static void
_win_aspect_set(Efl_Ui_Win_Data *sd, double aspect)
{
   sd->aspect = aspect;
   TRAP(sd, aspect_set, aspect);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

static double
_win_aspect_get(Efl_Ui_Win_Data *sd)
{
   return sd->aspect;
}

EOLIAN static void
_efl_ui_win_efl_gfx_hint_hint_aspect_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd,
                                              Efl_Gfx_Hint_Aspect mode, Eina_Size2D sz)
{
   if (sz.h) _win_aspect_set(pd, (double) sz.w / (double) sz.h);
   else _win_aspect_set(pd, 0.0);
   efl_gfx_hint_aspect_set(efl_super(obj, MY_CLASS), mode, sz);
#ifdef HAVE_ELEMENTARY_WL2
   if (pd->wl.win)
     ecore_wl2_window_aspect_set(pd->wl.win, sz.w, sz.h, mode);
#endif
}

EOLIAN static void
_efl_ui_win_efl_gfx_hint_hint_weight_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd EINA_UNUSED,
                                              double w, double h)
{
   efl_gfx_hint_weight_set(efl_super(obj, MY_CLASS), w, h);
#ifdef HAVE_ELEMENTARY_WL2
   if (pd->wl.win)
     ecore_wl2_window_weight_set(pd->wl.win, w, h);
#endif
}

EOLIAN static void
_efl_ui_win_hint_base_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D sz)
{
   sd->size_base_w = sz.w;
   sd->size_base_h = sz.h;
   TRAP(sd, size_base_set, sz.w, sz.h);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Eina_Size2D
_efl_ui_win_hint_base_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return EINA_SIZE2D(sd->size_base_w, sd->size_base_h);
}

EOLIAN static void
_efl_ui_win_hint_step_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Size2D sz)
{
   sd->size_step_w = sz.w;
   sd->size_step_h = sz.h;
   TRAP(sd, size_step_set, sz.w, sz.h);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static void
_efl_ui_win_efl_gfx_hint_hint_size_max_set(Eo *obj, Efl_Ui_Win_Data *sd, Eina_Size2D sz)
{
   if (sd->tmp_updating_hints)
     {
        efl_gfx_hint_size_max_set(efl_super(obj, MY_CLASS), sz);
     }
   else
     {
        if (sz.w < 1) sz.w = -1;
        if (sz.h < 1) sz.h = -1;
        sd->max_w = sz.w;
        sd->max_h = sz.h;
        _elm_win_resize_objects_eval(obj, EINA_FALSE);
     }
}

EOLIAN static Eina_Size2D
_efl_ui_win_hint_step_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return EINA_SIZE2D(sd->size_step_w, sd->size_step_h);
}

EAPI void
elm_win_norender_push(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->norender++;
   if (sd->norender == 1) ecore_evas_manual_render_set(sd->ee, EINA_TRUE);
}

EAPI void
elm_win_norender_pop(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (sd->norender <= 0) return;
   sd->norender--;
   if (sd->norender == 0) ecore_evas_manual_render_set(sd->ee, EINA_FALSE);
}

EAPI int
elm_win_norender_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return -1;

   return sd->norender;
}

EAPI void
elm_win_render(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   ecore_evas_manual_render(sd->ee);
}

EOLIAN static void
_efl_ui_win_wm_available_rotations_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd,
                                       Eina_Bool allow_0, Eina_Bool allow_90,
                                       Eina_Bool allow_180, Eina_Bool allow_270)
{
   unsigned cnt = 0;
   int rots[4];

   if (allow_0) rots[cnt++] = 0;
   if (allow_90) rots[cnt++] = 90;
   if (allow_180) rots[cnt++] = 180;
   if (allow_270) rots[cnt++] = 270;
   sd->wm_rot.use = EINA_TRUE;

   ELM_SAFE_FREE(sd->wm_rot.rots, free);
   sd->wm_rot.count = 0;

   if (cnt)
     {
        sd->wm_rot.rots = malloc(sizeof(int) * cnt);
        if (!sd->wm_rot.rots) return;
        memcpy(sd->wm_rot.rots, rots, cnt * sizeof(int));
        sd->wm_rot.count = cnt;
     }

   ecore_evas_wm_rotation_available_rotations_set(sd->ee,
                                                  sd->wm_rot.rots,
                                                  sd->wm_rot.count);
}

EOLIAN static Eina_Bool
_efl_ui_win_wm_available_rotations_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd,
                                       Eina_Bool *allow_0, Eina_Bool *allow_90,
                                       Eina_Bool *allow_180, Eina_Bool *allow_270)
{
   if (!sd->wm_rot.use) goto end;

   if (allow_0) *allow_0 = EINA_FALSE;
   if (allow_90) *allow_90 = EINA_FALSE;
   if (allow_180) *allow_180 = EINA_FALSE;
   if (allow_270) *allow_270 = EINA_FALSE;

   for (unsigned k = 0; k < sd->wm_rot.count; k++)
     {
        switch (sd->wm_rot.rots[k])
          {
           case 0: if (allow_0) *allow_0 = EINA_TRUE; break;
           case 90: if (allow_90) *allow_90 = EINA_TRUE; break;
           case 180: if (allow_180) *allow_180 = EINA_TRUE; break;
           case 270: if (allow_270) *allow_270 = EINA_TRUE; break;
           default: ERR("Unsupported rotation %d", sd->wm_rot.rots[k]); break;
          }
     }

end:
   return !!sd->wm_rot.use;
}

EAPI void
elm_win_wm_rotation_available_rotations_set(Elm_Win *obj, const int *rotations, unsigned int count)
{
   Eina_Bool allow[4] = { 0, };
   int found = 0;

   if (!rotations || !count) goto end;
   for (unsigned k = 0; (k < count) && (found < 4); k++)
     {
        int rot = (((rotations[k] % 360) + 360) % 360) / 90;
        if (!allow[rot])
          {
             allow[rot] = EINA_TRUE;
             found++;
          }
     }

end:
   efl_ui_win_wm_available_rotations_set(obj, allow[0], allow[1], allow[2], allow[3]);
}

EAPI Eina_Bool
elm_win_wm_rotation_available_rotations_get(const Elm_Win *obj, int **rotations, unsigned int *count)
{
   int rots[4] = { 0, };
   Eina_Bool allow[4] = { 0, };
   unsigned cnt = 0;

   if (!efl_ui_win_wm_available_rotations_get(obj, &allow[0], &allow[1], &allow[2], &allow[3]))
     goto none;

   for (int k = 0; k < 4; k++)
     if (allow[k])
       rots[cnt++] = k * 90;

   if (!cnt) goto none;

   if (rotations)
     {
        *rotations = malloc(sizeof(int) * cnt);
        if (!*rotations) goto none;
        memcpy(*rotations, rots, cnt * sizeof(int));
     }
   if (count) *count = cnt;
   return EINA_TRUE;

none:
   if (rotations) *rotations = NULL;
   if (count) *count = 0;
   return EINA_FALSE;
}

EOLIAN static void
_efl_ui_win_sticky_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool sticky)
{
//   sd->sticky = sticky;
   TRAP(sd, sticky_set, sticky);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EOLIAN static Eina_Bool
_efl_ui_win_sticky_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->sticky;
}

EOLIAN static void
_efl_ui_win_keyboard_mode_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Keyboard_Mode mode)
{
   if (mode == sd->kbdmode) return;
#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
#endif
   sd->kbdmode = mode;
#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_virtual_keyboard_state_set
          (sd->x.xwin, (Ecore_X_Virtual_Keyboard_State)sd->kbdmode);
     }
#endif
}

EOLIAN static Efl_Ui_Win_Keyboard_Mode
_efl_ui_win_keyboard_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->kbdmode;
}

EOLIAN static void
_efl_ui_win_indicator_mode_set(Eo *obj, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Indicator_Mode mode)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (sd->indimode == mode) return;
   sd->indimode = mode;

   if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_OFF)
     {
        _indicator_del(sd);
        return;
     }

   if (!sd->indicator) _indicator_add(sd);

   if (elm_widget_is_legacy(obj))
     {
        if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_BG_OPAQUE)
          edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,bg_opaque", "elm");
        else if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_BG_TRANSPARENT)
          edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,bg_transparent", "elm");
        else if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_HIDDEN)
          edje_object_signal_emit(sd->frame_obj, "elm,action,indicator,hidden", "elm");
     }
   else
     {
        if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_BG_OPAQUE)
          edje_object_signal_emit(sd->frame_obj, "efl,action,indicator,bg_opaque", "efl");
        else if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_BG_TRANSPARENT)
          edje_object_signal_emit(sd->frame_obj, "efl,action,indicator,bg_transparent", "efl");
        else if (sd->indimode == EFL_UI_WIN_INDICATOR_MODE_HIDDEN)
          edje_object_signal_emit(sd->frame_obj, "efl,action,indicator,hidden", "efl");
     }

   edje_object_message_signal_process(sd->frame_obj);
   evas_object_smart_calculate(sd->frame_obj);
   _elm_win_frame_obj_update(sd, 0);
}

EOLIAN static Efl_Ui_Win_Indicator_Mode
_efl_ui_win_indicator_mode_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   sd->legacy.forbidden = EINA_TRUE;
   return sd->indimode;
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_ui_focus_object_focus_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   // Bypass widget implementation here.
   return ecore_evas_focus_get(sd->ee);
}

EOLIAN static void
_efl_ui_win_screen_constrain_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool constrain)
{
   sd->constrain = !!constrain;
}

EOLIAN static Eina_Bool
_efl_ui_win_screen_constrain_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->constrain;
}

EOLIAN static Eina_Size2D
_efl_ui_win_efl_screen_screen_size_in_pixels_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   Eina_Size2D sz;
   ecore_evas_screen_geometry_get(sd->ee, NULL, NULL, &sz.w, &sz.h);
   return sz;
}

EOLIAN static void
_efl_ui_win_efl_screen_screen_dpi_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, int *xdpi, int *ydpi)
{
   ecore_evas_screen_dpi_get(sd->ee, xdpi, ydpi);
}

EOLIAN static int
_efl_ui_win_efl_screen_screen_rotation_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   //TODO: query to wm about device's rotation
   (void)sd;
   WRN("Not yet implemented");
   return 0;
}

EOLIAN static float
_efl_ui_win_efl_screen_screen_scale_factor_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   WRN("Not yet implemented");
   return 1.0;
}

EOLIAN static void
_efl_ui_win_prop_focus_skip_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool skip)
{
   sd->skip_focus = skip;
   TRAP(sd, focus_skip_set, skip);
}

EOLIAN static void
_efl_ui_win_focus_highlight_enabled_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool enabled)
{
   enabled = !!enabled;
   if (sd->focus_highlight.enabled == enabled)
     return;

   sd->focus_highlight.enabled = enabled;

   if ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled))
     _elm_win_focus_highlight_init(sd);
   else
     _elm_win_focus_highlight_shutdown(sd);
}

EOLIAN static Eina_Bool
_efl_ui_win_focus_highlight_enabled_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->focus_highlight.enabled;
}

static Eina_Error
_elm_win_theme_internal(Eo *obj, Efl_Ui_Win_Data *sd)
{
   Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
   Eina_Bool prev_alpha;
   const char *s;

   int_ret = elm_widget_theme_object_set(obj, sd->legacy.edje, "win", "base",
                                       elm_widget_style_get(obj));
   if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;

   edje_object_mirrored_set(sd->legacy.edje, efl_ui_mirrored_get(obj));
   edje_object_scale_set(sd->legacy.edje,
                         efl_gfx_entity_scale_get(obj) * elm_config_scale_get());

   efl_event_callback_legacy_call(obj, EFL_UI_WIN_EVENT_THEME_CHANGED, NULL);

   prev_alpha = sd->theme_alpha;
   s = edje_object_data_get(sd->legacy.edje, "alpha");
   sd->theme_alpha = (eina_streq(s, "1") || eina_streq(s, "true"));
   if (sd->theme_alpha != prev_alpha)
     _elm_win_apply_alpha(obj, sd);

   return int_ret;
}

EOLIAN static Eina_Error
_efl_ui_win_efl_ui_widget_theme_apply(Eo *obj, Efl_Ui_Win_Data *sd)
{
   Eina_Error int_ret = EFL_UI_THEME_APPLY_ERROR_GENERIC;
   int_ret = efl_ui_widget_theme_apply(efl_super(obj, MY_CLASS));
   if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;

   sd->focus_highlight.theme_changed = EINA_TRUE;

   int_ret = _elm_win_theme_internal(obj, sd) & int_ret;
   if (int_ret == EFL_UI_THEME_APPLY_ERROR_GENERIC) return int_ret;
   _elm_win_focus_highlight_reconfigure_job_start(sd);

   return int_ret;
}

EOLIAN static Eina_Bool
_efl_ui_win_focus_highlight_style_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *style)
{
   if (!eina_stringshare_replace(&sd->focus_highlight.style, style))
     return EINA_TRUE;

   sd->focus_highlight.theme_changed = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
   return EINA_TRUE;
}

EOLIAN static const char*
_efl_ui_win_focus_highlight_style_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->focus_highlight.style;
}

EOLIAN static void
_efl_ui_win_focus_highlight_animate_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool animate)
{
   animate = !!animate;
   if (sd->focus_highlight.animate == animate)
     return;

   sd->focus_highlight.animate = animate;
   sd->focus_highlight.theme_changed = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job_start(sd);
}

EOLIAN static Eina_Bool
_efl_ui_win_focus_highlight_animate_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->focus_highlight.animate;
}

EOLIAN static const char *
_efl_ui_win_stack_id_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->stack_id;
}

EOLIAN static void
_efl_ui_win_stack_master_id_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *id)
{
   if (sd->shown) return;
   eina_stringshare_replace(&(sd->stack_master_id), id);
#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     _elm_win_xwin_update(sd);
   else
#endif
     {
        int num = ecore_evas_aux_hint_id_get(sd->ee, "stack_master_id");
        if (num >= 0)
          ecore_evas_aux_hint_val_set(sd->ee, num, id);
        else
          ecore_evas_aux_hint_add(sd->ee, "stack_master_id", id);
     }
}

EOLIAN static const char *
_efl_ui_win_stack_master_id_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->stack_master_id;
}

EOLIAN static void
_efl_ui_win_stack_base_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eina_Bool base)
{
   int num;
   if (sd->shown) return;
   sd->stack_base = !!base;
   num = ecore_evas_aux_hint_id_get(sd->ee, "stack_base");
   if (num >= 0)
     ecore_evas_aux_hint_val_set(sd->ee, num, sd->stack_base ? "1" : "0");
   else
     ecore_evas_aux_hint_add(sd->ee, "stack_base", sd->stack_base ? "1" : "0");
}

EOLIAN static Eina_Bool
_efl_ui_win_stack_base_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   return sd->stack_base;
}

#ifdef HAVE_ELEMENTARY_X
// list transient from bottom to top by recursive walking
static void
_x_transients_for_list(Ecore_X_Window base, Ecore_X_Window transient,
                       Ecore_X_Window **wins, int *num)
{
   Ecore_X_Window t, *children, *w;
   int n, i;

   children = ecore_x_window_children_get(base, &n);
   if (children)
     {
        for (i = 0; i < n; i++)
          {
             if (children[i] != transient)
               {
                  t = ecore_x_icccm_transient_for_get(children[i]);
                  if (t == transient)
                    {
                       (*num)++;
                       w = realloc(*wins, *num * sizeof(Ecore_X_Window));
                       if (w)
                         {
                            *wins = w;
                            (*wins)[*num - 1] = children[i];
                         }
                    }
                  _x_transients_for_list(children[i], transient, wins, num);
               }
          }
        free(children);
     }
}
#endif

EOLIAN static void
_efl_ui_win_stack_pop_to(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd)
{
   if (!sd->stack_master_id) return;
#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     {
        Ecore_X_Window *wins = NULL;
        int i, num = 0;
        Eina_Bool del = EINA_FALSE;

        _internal_elm_win_xwindow_get(sd);
        ecore_x_grab();
        _x_transients_for_list
          (ecore_x_window_root_get(sd->x.xwin),
           ecore_x_icccm_transient_for_get(sd->x.xwin),
           &wins, &num);
        if (wins)
          {
             for (i = 0; i < num; i++)
               {
                  if (del) ecore_x_window_delete_request_send(wins[i]);
                  if (wins[i] == sd->x.xwin) del = EINA_TRUE;
               }
             free(wins);
          }
        ecore_x_ungrab();
     }
   else
#endif
     {
        int num = ecore_evas_aux_hint_id_get(sd->ee, "stack_pop_to");
        if (num >= 0) ecore_evas_aux_hint_val_set(sd->ee, num, "1");
        else ecore_evas_aux_hint_add(sd->ee, "stack_pop_to", "1");
     }
   // win32/osx ?
}

EAPI Eina_Bool
elm_win_trap_set(const Elm_Win_Trap *t)
{
   DBG("old %p, new %p", trap, t);

   if ((t) && (t->version != ELM_WIN_TRAP_VERSION))
     {
        CRI("trying to set a trap version %lu while %lu was expected!",
                 t->version, ELM_WIN_TRAP_VERSION);
        return EINA_FALSE;
     }

   trap = t;
   return EINA_TRUE;
}

EAPI void
elm_win_floating_mode_set(Evas_Object *obj, Eina_Bool floating)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   floating = !!floating;
   if (floating == sd->floating) return;
   sd->floating = floating;
#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        if (sd->floating)
          ecore_x_e_illume_window_state_set
             (sd->x.xwin, ECORE_X_ILLUME_WINDOW_STATE_FLOATING);
        else
          ecore_x_e_illume_window_state_set
             (sd->x.xwin, ECORE_X_ILLUME_WINDOW_STATE_NORMAL);
     }
#endif
}

EAPI Eina_Bool
elm_win_floating_mode_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->floating;
}

void
_elm_win_focus_highlight_in_theme_update(Evas_Object *obj, Eina_Bool in_theme)
{
   ELM_WIN_DATA_GET(obj, sd);
   sd->focus_highlight.cur.in_theme = !!in_theme;
}

void
_elm_win_focus_highlight_start(Evas_Object *obj)
{
   ELM_WIN_DATA_GET(obj, sd);

   if (!(sd->focus_highlight.enabled) && !(sd->focus_highlight.auto_enabled)) return;
   sd->focus_highlight.cur.visible = EINA_TRUE;
   sd->focus_highlight.geometry_changed = EINA_TRUE;
   _elm_win_focus_highlight_reconfigure_job(obj);
}

void
_elm_win_focus_auto_show(Evas_Object *obj)
{
   ELM_WIN_DATA_GET(obj, sd);
   Eina_Bool pfocus = (sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled);
   sd->focus_highlight.auto_enabled = _elm_config->win_auto_focus_enable;
   sd->focus_highlight.auto_animate = _elm_config->win_auto_focus_animate;
   if (pfocus != ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled)))
     {
        if ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled))
          _elm_win_focus_highlight_init(sd);
     }
}

void
_elm_win_focus_auto_hide(Evas_Object *obj)
{
   ELM_WIN_DATA_GET(obj, sd);
   Eina_Bool pfocus = (sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled);
   sd->focus_highlight.auto_enabled = EINA_FALSE;
   sd->focus_highlight.auto_animate = EINA_FALSE;
   if (pfocus != ((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled)))
     {
        if (!((sd->focus_highlight.enabled) || (sd->focus_highlight.auto_enabled)))
          _elm_win_focus_highlight_shutdown(sd);
     }
}

static void
_on_atspi_bus_connected(void *data EINA_UNUSED, const Efl_Event *event EINA_UNUSED)
{
   Evas_Object *win;
   Eina_List *l;

   EINA_LIST_FOREACH(_elm_win_list, l, win)
     {
        /**
         * Reemit accessibility events when AT-SPI2 connection is begin
         * established. This assures that Assistive Technology clients will
         * receive all org.a11y.window events and could keep track of active
         * windows whithin system.
         */
        efl_access_window_created_signal_emit(win);
        if (elm_win_focus_get(win))
          {
             Evas_Object *target;
             efl_access_window_activated_signal_emit(win);
             /** Reemit focused event to inform atspi clients about currently
              * focused object **/
             {
                Efl_Ui_Focus_Manager *m;

                m = win;

                while (efl_ui_focus_manager_redirect_get(m))
                  m = efl_ui_focus_manager_redirect_get(m);

                target = efl_ui_focus_manager_focus_get(m);
             }
             if (target)
               efl_access_state_changed_signal_emit(target, EFL_ACCESS_STATE_TYPE_FOCUSED, EINA_TRUE);
          }
        else
          efl_access_window_deactivated_signal_emit(win);
     }
}

EOLIAN static void
_efl_ui_win_class_constructor(Efl_Class *klass EINA_UNUSED)
{
   if (_elm_config->atspi_mode)
     {
        Eo *bridge = _elm_atspi_bridge_get();
        if (bridge)
           efl_event_callback_add(bridge, ELM_ATSPI_BRIDGE_EVENT_CONNECTED, _on_atspi_bus_connected, NULL);
     }
}

EOLIAN static void
_efl_ui_win_efl_object_debug_name_override(Eo *obj, Efl_Ui_Win_Data *pd, Eina_Strbuf *sb)
{
   efl_debug_name_override(efl_super(obj, MY_CLASS), sb);
   eina_strbuf_append_printf(sb, ":'%s':'%s'", pd->name, pd->title);
}

EOLIAN static const Efl_Access_Action_Data*
_efl_ui_win_efl_access_widget_action_elm_actions_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   static Efl_Access_Action_Data atspi_actions[] = {
          { "move,previous", "move", "previous", _key_action_move},
          { "move,next", "move", "next", _key_action_move},
          { "move,left", "move", "left", _key_action_move},
          { "move,right", "move", "right", _key_action_move},
          { "move,up", "move", "up", _key_action_move},
          { "move,down", "move", "down", _key_action_move},
          { NULL, NULL, NULL, NULL }
   };
   return &atspi_actions[0];
}

EOLIAN static Efl_Access_State_Set
_efl_ui_win_efl_access_object_state_set_get(const Eo *obj, Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   Efl_Access_State_Set ret;
   ret = efl_access_object_state_set_get(efl_super(obj, MY_CLASS));

   if (elm_win_focus_get(obj))
     STATE_TYPE_SET(ret, EFL_ACCESS_STATE_TYPE_ACTIVE);

   return ret;
}

EOLIAN static const char*
_efl_ui_win_efl_access_object_i18n_name_get(const Eo *obj, Efl_Ui_Win_Data *sd EINA_UNUSED)
{
   const char *ret;
   ret = efl_access_object_i18n_name_get(efl_super(obj, EFL_UI_WIN_CLASS));
   if (ret) return ret;
   const char *name = elm_win_title_get(obj);
   return name;
}

EOLIAN static Eina_Rect
_efl_ui_win_efl_access_component_extents_get(const Eo *obj, Efl_Ui_Win_Data *_pd EINA_UNUSED, Eina_Bool screen_coords)
{
   Eina_Rect r;

   r = efl_gfx_entity_geometry_get(obj);
   r.x = r.y = 0;
   if (screen_coords)
     {
        r = _efl_access_component_screen_coords_extents_get(obj, r);
     }
   return r;
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_input_state_modifier_enabled_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd,
                                                 Efl_Input_Modifier mod, const Efl_Input_Device *seat)
{
   const Evas_Modifier *m = evas_key_modifier_get(pd->evas);
   const char *name = _efl_input_modifier_to_string(mod);
   return evas_seat_key_modifier_is_set(m, name, seat);
}

EOLIAN static Eina_Bool
_efl_ui_win_efl_input_state_lock_enabled_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *pd,
                                             Efl_Input_Lock lock, const Efl_Input_Device *seat)
{
   const Evas_Lock *m = evas_key_lock_get(pd->evas);
   const char *name = _efl_input_lock_to_string(lock);
   return evas_seat_key_lock_is_set(m, name, seat);
}

EOLIAN static Efl_Object *
_efl_ui_win_efl_object_provider_find(const Eo *obj,
                                     Efl_Ui_Win_Data *pd EINA_UNUSED,
                                     const Efl_Object *klass)
{
    if (klass == EFL_UI_WIN_CLASS)
      return (Eo *)obj;

   // attach all kinds of windows directly to ATSPI application root object
   if (klass == EFL_ACCESS_OBJECT_MIXIN) return efl_access_object_access_root_get();

    if (klass == EFL_UI_FOCUS_PARENT_PROVIDER_INTERFACE)
      return pd->provider;

    return efl_provider_find(efl_super(obj, MY_CLASS), klass);
}

// See evas_inline.x
#define _EVAS_COLOR_CLAMP(x, y) do { \
if (x > y) { x = y; bad = 1; } \
if (x < 0) { x = 0; bad = 1; } } while (0)

#define EVAS_COLOR_SANITIZE(r, g, b, a) \
({ int bad = 0; \
   _EVAS_COLOR_CLAMP(a, 255); \
   _EVAS_COLOR_CLAMP(r, a); \
   _EVAS_COLOR_CLAMP(g, a); \
   _EVAS_COLOR_CLAMP(b, a); \
   bad; })

/* Efl.Part APIs */

#define WIN_PART_ERR(part) ERR("No such part in window: '%s'. Supported parts are: 'content' and 'background'.", part);

static Eina_Bool
_elm_win_bg_set(Efl_Ui_Win_Data *sd, Eo *bg)
{
   ELM_SAFE_DEL(sd->bg);
   if (!bg) return EINA_TRUE;

   if (!elm_widget_sub_object_add(sd->obj, bg))
     return EINA_FALSE;
   if (elm_widget_is_legacy(sd->obj))
     {
        if (!_elm_config->win_no_border)
          {
             if (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.background", bg))
               return EINA_FALSE;
          }
        else
          {
             if (!edje_object_part_swallow(sd->legacy.edje, "elm.swallow.background", bg))
               return EINA_FALSE;
          }
     }
   else
     {
        if (!_elm_config->win_no_border)
          {
             if (!edje_object_part_swallow(sd->frame_obj, "efl.background", bg))
               return EINA_FALSE;
          }
        else
          {
             if (!edje_object_part_swallow(sd->legacy.edje, "efl.background", bg))
               return EINA_FALSE;
          }
     }
   efl_gfx_entity_visible_set(bg, 1);
   efl_gfx_hint_fill_set(bg, EINA_TRUE, EINA_TRUE);
   efl_gfx_hint_weight_set(bg, 1, 1);
   efl_wref_add(bg, &sd->bg);
   return EINA_TRUE;
}

/* Legacy theme compatibility */
static Eina_Bool
_elm_win_bg_must_swallow(Efl_Ui_Win_Data *sd, Eo **bg)
{
   if (EINA_UNLIKELY(!sd->legacy.bg_must_swallow_init))
     {
        /* Overkill: check which theme version the standard elm_bg uses */
        Elm_Widget_Smart_Data *wd;
        const char *version;
        int v;

        sd->legacy.bg_must_swallow = 1;
        sd->legacy.bg_must_swallow_init = 1;

        if (sd->legacy.ctor)
          *bg = elm_bg_add(sd->obj);
        else
          {
             // Note: This code path is probably not necessary (custom legacy
             // theme but efl_add'ed window -- all efl_add'ed widgets would
             // use default theme)
             *bg = efl_add(EFL_UI_BG_CLASS, sd->obj);
          }
        wd = efl_data_scope_get(*bg, EFL_UI_WIDGET_CLASS);
        if (wd)
          {
             version = edje_object_data_get(wd->resize_obj, "version");
             v = version ? atoi(version) : 0;
             if (v >= FRAME_OBJ_THEME_MIN_VERSION)
               sd->legacy.bg_must_swallow = 0;
          }
     }

   return sd->legacy.bg_must_swallow;
}

void
_elm_win_standard_init(Eo *obj)
{
   /* Support for elm_util_win_standard_add() and Efl.Ui.Win.Standard */
   Efl_Ui_Win_Data *sd = efl_data_scope_get(obj, MY_CLASS);
   Eo *bg = NULL;

   ELM_SAFE_DEL(sd->bg);

   sd->csd.need_bg_standard = 1;
   if (!_elm_win_bg_must_swallow(sd, &bg))
     {
        sd->csd.need_bg_solid = EINA_TRUE;
        evas_object_del(bg);
     }
   else
     {
        /* Legacy theme compatibility */
        DBG("Detected legacy theme used for elm_bg. Swallowing object.");
        sd->csd.need_bg_solid = EINA_FALSE;

        if (!bg)
          {
             if (sd->legacy.ctor)
               bg = elm_bg_add(obj);
             else
               {
                  // Note: This code path is probably not necessary (custom legacy
                  // theme but efl_add'ed window -- all efl_add'ed widgets would
                  // use default theme)
                  bg = efl_add(EFL_UI_BG_CLASS, obj);
               }
          }
        _elm_win_bg_set(sd, bg);
     }

   _elm_win_frame_style_update(sd, 0, 1);
}

static Eina_Bool
_efl_ui_win_content_set(Eo *obj, Efl_Ui_Win_Data *sd, const char *part, Eo *content)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (eina_streq(part, "content"))
     {
        if (sd->content == content) return EINA_TRUE;
        if (content && !elm_widget_sub_object_add(obj, content))
          goto err;
        /* FIXME: Switch to swallow inside the frame
        if (!edje_object_part_swallow(sd->frame_obj, "elm.swallow.client", content))
          goto err;
        */
        evas_object_box_append(sd->legacy.box, content);
        evas_object_show(content);
        efl_wref_add(content, &sd->content);
        return EINA_TRUE;
     }
   else if (eina_streq(part, "background"))
     {
        sd->csd.need_bg_standard = 0;
        if (sd->bg == content) return EINA_TRUE;
        if (!_elm_win_bg_set(sd, content))
          goto err;
        return EINA_TRUE;
     }

   WIN_PART_ERR(part);
   return EINA_FALSE;

err:
   ERR("Failed to set object %p as %s for window %p", content, part, obj);
   return EINA_FALSE;
}

static Efl_Canvas_Object *
_efl_ui_win_content_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (eina_streq(part, "content"))
     return sd->content;
   else if (eina_streq(part, "background"))
     return sd->bg;

   WIN_PART_ERR(part);
   return NULL;
}

static Efl_Canvas_Object *
_efl_ui_win_content_unset(Eo *obj, Efl_Ui_Win_Data *sd, const char *part)
{
   Eo *content;

   sd->legacy.forbidden = EINA_TRUE;
   content = _efl_ui_win_content_get(obj, sd, part);
   if (!content) return NULL;

   efl_ref(content);
   _efl_ui_win_content_set(obj, sd, part, NULL);
   return content;
}

static Eina_Bool
_efl_ui_win_part_color_set(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part, int r, int g, int b, int a)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (eina_streq(part, "background"))
     {
        sd->csd.need_bg_solid = EINA_TRUE;
        edje_object_color_class_set(sd->frame_obj, "elm/win/background", r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0);
        _elm_win_frame_style_update(sd, 0, 1);
        return EINA_TRUE;
     }

   WIN_PART_ERR(part);
   return EINA_FALSE;
}

static Eina_Bool
_efl_ui_win_part_color_get(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const char *part, int *r, int *g, int *b, int *a)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (eina_streq(part, "background"))
     {
        edje_object_color_class_get(sd->frame_obj, "elm/win/background", r, g, b, a, 0, 0, 0, 0, 0, 0, 0, 0);
        return EINA_TRUE;
     }

   WIN_PART_ERR(part);
   return EINA_FALSE;
}

static void
_efl_ui_win_part_file_unload(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Eo *part_obj EINA_UNUSED, const char *part)
{
   sd->legacy.forbidden = EINA_TRUE;
   if (eina_streq(part, "background"))
     {
        _elm_win_bg_set(sd, NULL);
        return;
     }

   WIN_PART_ERR(part);
}

static Eina_Error
_efl_ui_win_part_file_load(Eo *obj, Efl_Ui_Win_Data *sd, Eo *part_obj, const char *part)
{
   const char *file, *key;

   sd->legacy.forbidden = EINA_TRUE;
   if (efl_file_loaded_get(part_obj)) return 0;
   file = efl_file_get(part_obj);
   key = efl_file_key_get(part_obj);
   if (eina_streq(part, "background"))
     {
        Eina_Bool ok = EINA_TRUE;
        Eo *bg = NULL;

        if (file)
          {
             bg = efl_add(EFL_UI_IMAGE_CLASS, obj);
             efl_gfx_image_scale_method_set(bg, EFL_GFX_IMAGE_SCALE_METHOD_EXPAND);
             ok = efl_file_simple_load(bg, file, key);
             if (!ok) ELM_SAFE_DEL(bg);
             _elm_win_bg_set(sd, bg);
          }
        else
          {
             _elm_win_standard_init(obj);
          }

        return ok;
     }

   WIN_PART_ERR(part);
   return EINA_FALSE;
}

static const char *
_efl_ui_win_part_file_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const Eo *part_obj, const char *part EINA_UNUSED)
{
   sd->legacy.forbidden = EINA_TRUE;
   return efl_file_get(efl_super(part_obj, EFL_UI_WIN_PART_CLASS));
#if 0

   if (eina_streq(part, "background"))
     {
        const Eo *bg = _efl_ui_win_content_get(obj, sd, "background");
        return efl_file_get(bg);
     }

   WIN_PART_ERR(part);
   return NULL;
#endif
}

static const char *
_efl_ui_win_part_file_key_get(const Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, const Eo *part_obj, const char *part EINA_UNUSED)
{
   sd->legacy.forbidden = EINA_TRUE;
   return efl_file_key_get(efl_super(part_obj, EFL_UI_WIN_PART_CLASS));
/* NOTE; if more than one part is ever supported here then this section is needed */
#if 0

   if (eina_streq(part, "background"))
     {
        const Eo *bg = _efl_ui_win_content_get(obj, sd, "background");
        return efl_file_get(bg);
     }

   WIN_PART_ERR(part);
   return NULL;
#endif
}

/* Efl.Part begin */

static void
_efl_ui_win_part_efl_gfx_color_color_set(Eo *obj, void *_pd EINA_UNUSED, int r, int g, int b, int a)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);

   if (EVAS_COLOR_SANITIZE(r, g, b, a))
     ERR("Evas only handles premultiplied colors (0 <= R,G,B <= A <= 255)");

   _efl_ui_win_part_color_set(pd->obj, sd, pd->part, r, g, b, a);
}

static void
_efl_ui_win_part_efl_gfx_color_color_get(const Eo *obj, void *_pd EINA_UNUSED, int *r, int *g, int *b, int *a)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);
   _efl_ui_win_part_color_get(pd->obj, sd, pd->part, r, g, b, a);
}

EOLIAN static const char *
_efl_ui_win_part_efl_file_file_get(const Eo *obj, void *_pd EINA_UNUSED)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);
   return _efl_ui_win_part_file_get(pd->obj, sd, obj, pd->part);
}

EOLIAN static const char *
_efl_ui_win_part_efl_file_key_get(const Eo *obj, void *_pd EINA_UNUSED)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);
   return _efl_ui_win_part_file_key_get(pd->obj, sd, obj, pd->part);
}

EOLIAN static void
_efl_ui_win_part_efl_file_unload(Eo *obj, void *_pd EINA_UNUSED)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);
   return _efl_ui_win_part_file_unload(pd->obj, sd, obj, pd->part);
}

EOLIAN static Eina_Error
_efl_ui_win_part_efl_file_load(Eo *obj, void *_pd EINA_UNUSED)
{
   Elm_Part_Data *pd = efl_data_scope_get(obj, EFL_UI_WIDGET_PART_CLASS);
   Efl_Ui_Win_Data *sd = efl_data_scope_get(pd->obj, MY_CLASS);
   return _efl_ui_win_part_file_load(pd->obj, sd, obj, pd->part);
}


ELM_PART_OVERRIDE(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data)
ELM_PART_OVERRIDE_CONTENT_SET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data)
ELM_PART_OVERRIDE_CONTENT_GET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data)
ELM_PART_OVERRIDE_CONTENT_UNSET(efl_ui_win, EFL_UI_WIN, Efl_Ui_Win_Data)
ELM_PART_CONTENT_DEFAULT_GET(efl_ui_win, "content")
ELM_PART_CONTENT_DEFAULT_IMPLEMENT(efl_ui_win, Efl_Ui_Win_Data)
#include "efl_ui_win_part.eo.c"

/* Efl.Part end */

EOLIAN static Eina_Bool
_efl_ui_win_move_resize_start(Eo *obj EINA_UNUSED, Efl_Ui_Win_Data *sd, Efl_Ui_Win_Move_Resize_Mode mode)
{
   // 1. move_resize can only be started after mouse down event
   if (evas_event_down_count_get(sd->evas) <= 0)
     {
        ERR("move_resize_start can only be called when a pointer is pressed.");
        return EINA_FALSE;
     }
   return _win_move_resize_start(sd, mode);
}

EAPI Evas_Object *
elm_win_get(Evas_Object *obj)
{
   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
   return ecore_evas_data_get(ecore_evas_ecore_evas_get(evas_object_evas_get(obj)), "elm_win");
}

/* windowing specific calls - shall we do this differently? */

EAPI Ecore_X_Window
elm_win_xwindow_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return 0;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin) return sd->x.xwin;
   if (sd->parent) return elm_win_xwindow_get(sd->parent);
#endif
   return 0;
}

EAPI Ecore_Wl2_Window *
elm_win_wl_window_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);

   if (!sd) return NULL;

   if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY))
     {
        Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
        return _elm_ee_wlwin_get(ee);
     }

#if HAVE_ELEMENTARY_WL2
   if (sd->wl.win) return sd->wl.win;
   if (sd->parent) return elm_win_wl_window_get(sd->parent);
#endif

   return NULL;
}

EAPI Ecore_Cocoa_Window *
elm_win_cocoa_window_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

#if HAVE_ELEMENTARY_COCOA
   if (sd->cocoa.win) return sd->cocoa.win;
   if (sd->ee) return _elm_ee_cocoa_win_get(sd->ee);
   if (sd->parent) return elm_win_cocoa_window_get(sd->parent);
#endif

   Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
   return _elm_ee_cocoa_win_get(ee);

   return NULL;
}

EAPI Ecore_Win32_Window *
elm_win_win32_window_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   const char *engine_name;

   if (!sd) return NULL;
   engine_name = ecore_evas_engine_name_get(sd->ee);
   if (!(engine_name &&
         ((!strcmp(engine_name, ELM_SOFTWARE_WIN32)) ||
          (!strcmp(engine_name, ELM_SOFTWARE_DDRAW)))))
     return NULL;

   if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY))
     {
        Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
        return _elm_ee_win32win_get(ee);
     }

   Ecore_Win32_Window *ret = NULL;

#if HAVE_ELEMENTARY_WIN32
   if (sd->win32.win) ret = sd->win32.win;
   if (sd->parent) ret = elm_win_win32_window_get(sd->parent);
#endif

   return ret;
}

EAPI void *
elm_win_trap_data_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   return sd->trap_data;
}

EAPI void
elm_win_override_set(Evas_Object *obj, Eina_Bool override)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   TRAP(sd, override_set, override);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI Eina_Bool
elm_win_override_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return ecore_evas_override_get(sd->ee);
}

EAPI void
elm_win_lower(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   TRAP(sd, lower);
}

EAPI void
elm_win_quickpanel_set(Evas_Object *obj, Eina_Bool quickpanel)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_illume_quickpanel_set(sd->x.xwin, quickpanel);
        if (quickpanel)
          {
             Ecore_X_Window_State states[2];

             states[0] = ECORE_X_WINDOW_STATE_SKIP_TASKBAR;
             states[1] = ECORE_X_WINDOW_STATE_SKIP_PAGER;
             ecore_x_netwm_window_state_set(sd->x.xwin, states, 2);
             ecore_x_icccm_hints_set(sd->x.xwin, 0, 0, 0, 0, 0, 0, 0);
          }
     }
#else
   (void)sd;
   (void)quickpanel;
#endif
}

EAPI Eina_Bool
elm_win_quickpanel_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_illume_quickpanel_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif

   return EINA_FALSE;
}

EAPI void
elm_win_quickpanel_priority_major_set(Evas_Object *obj, int priority)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_illume_quickpanel_priority_major_set(sd->x.xwin, priority);
     }
#else
   (void)sd;
   (void)priority;
#endif
}

EAPI int
elm_win_quickpanel_priority_major_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return -1;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_illume_quickpanel_priority_major_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif

   return -1;
}

EAPI void
elm_win_quickpanel_priority_minor_set(Evas_Object *obj, int priority)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_illume_quickpanel_priority_minor_set(sd->x.xwin, priority);
     }
#else
   (void)sd;
   (void)priority;
#endif
}

EAPI int
elm_win_quickpanel_priority_minor_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return -1;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_illume_quickpanel_priority_minor_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif

   return -1;
}

EAPI void
elm_win_quickpanel_zone_set(Evas_Object *obj, int zone)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_illume_quickpanel_zone_set(sd->x.xwin, zone);
     }
#else
   (void)sd;
   (void)zone;
#endif
}

EAPI int
elm_win_quickpanel_zone_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return 0;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_illume_quickpanel_zone_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif

   return 0;
}

EAPI void
elm_win_indicator_mode_set(Evas_Object *obj, Elm_Win_Indicator_Mode mode)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return;
     }

   if (mode == sd->legacy.indmode) return;
#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
#endif
   sd->legacy.indmode = mode;
#ifdef HAVE_ELEMENTARY_X
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        if (sd->legacy.indmode == ELM_WIN_INDICATOR_SHOW)
          ecore_x_e_illume_indicator_state_set
            (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_ON);
        else if (sd->legacy.indmode == ELM_WIN_INDICATOR_HIDE)
          ecore_x_e_illume_indicator_state_set
            (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_STATE_OFF);
     }
#endif
   efl_event_callback_legacy_call
     (obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL);
}

EAPI Elm_Win_Indicator_Mode
elm_win_indicator_mode_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return ELM_WIN_INDICATOR_UNKNOWN;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return ELM_WIN_INDICATOR_UNKNOWN;
     }

   return sd->legacy.indmode;
}

EAPI void
elm_win_indicator_opacity_set(Evas_Object *obj, Elm_Win_Indicator_Opacity_Mode mode)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return;
     }

   if (mode == sd->legacy.ind_o_mode) return;
   sd->legacy.ind_o_mode = mode;
#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_OPAQUE)
          ecore_x_e_illume_indicator_opacity_set
            (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_OPAQUE);
        else if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_TRANSLUCENT)
          ecore_x_e_illume_indicator_opacity_set
            (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_TRANSLUCENT);
        else if (sd->legacy.ind_o_mode == ELM_WIN_INDICATOR_TRANSPARENT)
          ecore_x_e_illume_indicator_opacity_set
            (sd->x.xwin, ECORE_X_ILLUME_INDICATOR_TRANSPARENT);
     }
#endif
   efl_event_callback_legacy_call
     (obj, EFL_UI_WIN_EVENT_INDICATOR_PROP_CHANGED, NULL);
}

EAPI Elm_Win_Indicator_Opacity_Mode
elm_win_indicator_opacity_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return ELM_WIN_INDICATOR_OPACITY_UNKNOWN;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return ELM_WIN_INDICATOR_OPACITY_UNKNOWN;
     }

   return sd->legacy.ind_o_mode;
}

EAPI void
elm_win_keyboard_win_set(Evas_Object *obj, Eina_Bool is_keyboard)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_virtual_keyboard_set(sd->x.xwin, is_keyboard);
     }
#else
   (void)sd;
   (void)is_keyboard;
#endif
}

EAPI Eina_Bool
elm_win_keyboard_win_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_virtual_keyboard_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif
   return EINA_FALSE;
}

EAPI void
elm_win_conformant_set(Evas_Object *obj, Eina_Bool conformant)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ecore_x_e_illume_conformant_set(sd->x.xwin, conformant);
     }
#else
   (void)sd;
   (void)conformant;
#endif
}

EAPI Eina_Bool
elm_win_conformant_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return ecore_x_e_illume_conformant_get(sd->x.xwin);
     }
#else
   (void)sd;
#endif

   return EINA_FALSE;
}

EAPI void
elm_win_wm_rotation_manual_rotation_done_set(Evas_Object *obj, Eina_Bool set)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (!sd->wm_rot.use) return;
   ecore_evas_wm_rotation_manual_rotation_done_set(sd->ee, set);
}

EAPI Eina_Bool
elm_win_wm_rotation_manual_rotation_done_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   if (!sd->wm_rot.use) return EINA_FALSE;
   return ecore_evas_wm_rotation_manual_rotation_done_get(sd->ee);
}

EAPI void
elm_win_wm_rotation_manual_rotation_done(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (!sd->wm_rot.use) return;
   ecore_evas_wm_rotation_manual_rotation_done(sd->ee);
}

/*
 * This API does not resize the internal window (ex: X window).
 * But this resizes evas_output, elm window, and its contents.
 */
EAPI void
elm_win_rotation_with_resize_set(Evas_Object *obj, int rotation)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   _win_rotate(obj, sd, rotation, EINA_TRUE);
}

EAPI int
elm_win_wm_rotation_preferred_rotation_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return -1;

   return sd->wm_rot.preferred_rot;
}

EAPI Eina_Bool
elm_win_wm_rotation_supported_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->wm_rot.wm_supported;
}

EAPI void
elm_win_wm_rotation_preferred_rotation_set(Evas_Object *obj, int rotation)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   int rot;

   if (!sd) return;
   if (!sd->wm_rot.use)
     sd->wm_rot.use = EINA_TRUE;

   // '-1' means that elm_win doesn't use preferred rotation.
   if (rotation == -1)
     rot = -1;
   else
     rot = _win_rotation_degree_check(rotation);

   if (sd->wm_rot.preferred_rot == rot) return;
   sd->wm_rot.preferred_rot = rot;

   ecore_evas_wm_rotation_preferred_rotation_set(sd->ee, rot);
}

EAPI void
elm_win_screen_size_get(const Evas_Object *obj, int *x, int *y, int *w, int *h)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   ecore_evas_screen_geometry_get(sd->ee, x, y, w, h);
}

EAPI void
elm_win_screen_position_get(const Evas_Object *obj, int *x, int *y)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (x) *x = sd->screen.x;
   if (y) *y = sd->screen.y;
}

EAPI void
elm_win_screen_dpi_get(const Evas_Object *obj, int *xdpi, int *ydpi)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   ecore_evas_screen_dpi_get(sd->ee, xdpi, ydpi);
}

EAPI void
elm_win_icon_name_set(Evas_Object *obj, const char *icon_name)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (!icon_name) return;
   eina_stringshare_replace(&(sd->icon_name), icon_name);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI const char*
elm_win_icon_name_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   return sd->icon_name;
}

EAPI void
elm_win_withdrawn_set(Evas_Object *obj, Eina_Bool withdrawn)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

//   sd->withdrawn = withdrawn;
   TRAP(sd, withdrawn_set, withdrawn);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI Eina_Bool
elm_win_withdrawn_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;
   if (ecore_evas_override_get(sd->ee))
     {
        if (evas_object_visible_get(obj)) return EINA_FALSE;
        return EINA_FALSE;
     }
   return sd->withdrawn;
}

EAPI void
elm_win_urgent_set(Evas_Object *obj, Eina_Bool urgent)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   if (sd->urgent == urgent)
     return;
   sd->urgent = urgent;
   TRAP(sd, urgent_set, urgent);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI Eina_Bool
elm_win_urgent_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->urgent;
}

EAPI void
elm_win_demand_attention_set(Evas_Object *obj, Eina_Bool demand_attention)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->demand_attention = demand_attention;
   TRAP(sd, demand_attention_set, demand_attention);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI Eina_Bool
elm_win_demand_attention_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return sd->demand_attention;
}

EAPI void
elm_win_modal_set(Evas_Object *obj, Eina_Bool modal)
{
   Efl_Ui_Win_Modal_Mode modality;

   modality = modal ? EFL_UI_WIN_MODAL_MODE_MODAL : EFL_UI_WIN_MODAL_MODE_NONE;
   efl_ui_win_modal_set(obj, modality);
}

EAPI Eina_Bool
elm_win_modal_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Modal_Mode modality;

   modality = efl_ui_win_modal_get(obj);
   return (modality != EFL_UI_WIN_MODAL_MODE_NONE);
}

EAPI void
elm_win_shaped_set(Evas_Object *obj, Eina_Bool shaped)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   shaped = !!shaped;

#ifdef HAVE_ELEMENTARY_X
   if (sd->x.shaped == shaped) return;
   sd->x.shaped = shaped;
   TRAP(sd, shaped_set, shaped);
#endif
}

EAPI Eina_Bool
elm_win_shaped_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return EINA_FALSE;

   return ecore_evas_shaped_get(sd->ee);
}

EAPI void
elm_win_title_set(Evas_Object *obj, const char *title)
{
   efl_text_set(obj, title);
}

EAPI const char*
elm_win_title_get(const Evas_Object *obj)
{
   return efl_text_get(obj);
}

EAPI void
elm_win_size_base_set(Evas_Object *obj, int w, int h)
{
   efl_ui_win_hint_base_set(obj, EINA_SIZE2D(w, h));
}

EAPI void
elm_win_size_base_get(const Evas_Object *obj, int *w, int *h)
{
   Eina_Size2D sz;
   sz = efl_ui_win_hint_base_get(obj);
   if (w) *w = sz.w;
   if (h) *h = sz.h;
}

EAPI void
elm_win_size_step_set(Evas_Object *obj, int w, int h)
{
   efl_ui_win_hint_step_set(obj, EINA_SIZE2D(w, h));
}

EAPI void
elm_win_size_step_get(const Evas_Object *obj, int *w, int *h)
{
   Eina_Size2D sz;
   sz = efl_ui_win_hint_step_get(obj);
   if (w) *w = sz.w;
   if (h) *h = sz.h;
}

EAPI void
elm_win_illume_command_send(Evas_Object *obj, Elm_Illume_Command command, void *params EINA_UNUSED)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        switch (command)
          {
           case ELM_ILLUME_COMMAND_FOCUS_BACK:
             ecore_x_e_illume_focus_back_send(sd->x.xwin);
             break;

           case ELM_ILLUME_COMMAND_FOCUS_FORWARD:
             ecore_x_e_illume_focus_forward_send(sd->x.xwin);
             break;

           case ELM_ILLUME_COMMAND_FOCUS_HOME:
             ecore_x_e_illume_focus_home_send(sd->x.xwin);
             break;

           case ELM_ILLUME_COMMAND_CLOSE:
             ecore_x_e_illume_close_send(sd->x.xwin);
             break;

           default:
             break;
          }
     }
#else
   (void)sd;
   (void)command;
#endif
}

EAPI void
elm_win_profile_set(Evas_Object *obj, const char *profile)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   /* check to see if a given profile is present in an available profiles */
   if (profile && eina_array_count(sd->profile.available))
     {
        if (!_profile_exists(sd, profile))
          return;
     }

   if (ecore_evas_window_profile_supported_get(sd->ee))
     {
        if (!profile) _elm_win_profile_del(sd);
        ecore_evas_window_profile_set(sd->ee, profile);
     }
   else
     {
        if (_internal_elm_win_profile_set(sd, profile))
          _elm_win_profile_update(sd);
     }
}

EAPI const char*
elm_win_profile_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   return sd->profile.name;
}

EAPI void
elm_win_layer_set(Evas_Object *obj, int layer)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   TRAP(sd, layer_set, layer);
#ifdef HAVE_ELEMENTARY_X
   _elm_win_xwin_update(sd);
#endif
}

EAPI int
elm_win_layer_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return 0;

   return ecore_evas_layer_get(sd->ee);
}

EAPI Evas_Object*
elm_win_inlined_image_object_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

   return sd->img_obj;
}

static Ecore_Window
_elm_win_window_id_get(Efl_Ui_Win_Data *sd)
{
#if HAVE_ELEMENTARY_WL2
   if (sd->wl.win)
     return (Ecore_Window)sd->wl.win;
   if (sd->parent)
     {
        Ecore_Wl2_Window *parent;

        parent = elm_win_wl_window_get(sd->parent);
        if (parent)
          return (Ecore_Window)parent;
     }
#endif
#ifdef HAVE_ELEMENTARY_X
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        return (Ecore_Window)sd->x.xwin;
     }
   if (sd->parent)
     {
        Ecore_Window xwin = elm_win_xwindow_get(sd->parent);
        if (xwin) return xwin;
     }
#endif
#ifdef HAVE_ELEMENTARY_COCOA
   if (sd->cocoa.win) return (Ecore_Window)(sd->cocoa.win);
   if (sd->parent)
     {
        Ecore_Cocoa_Window *pwin;
        pwin = elm_win_cocoa_window_get(sd->parent);
        if (pwin) return (Ecore_Window)pwin;
     }
#endif
#ifdef HAVE_ELEMENTARY_WIN32
        _internal_elm_win_win32window_get(sd);
        if (sd->win32.win) return (Ecore_Window)sd->win32.win;
        if (sd->parent)
          {
             Ecore_Window wwin = (Ecore_Window)elm_win_win32_window_get(sd->parent);
             if (wwin) return wwin;
          }
#endif

   return 0;
}

EAPI Ecore_Window
elm_win_window_id_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd;
   if (!obj) return 0;

   if (!evas_object_smart_type_check_ptr(obj, MY_CLASS_NAME_LEGACY))
     {
        Ecore_Evas *ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj));
        if (!ee) return 0;
        return ecore_evas_window_get(ee);
     }

   sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return 0;
   return _elm_win_window_id_get(sd);
}

EAPI Evas_Object *
elm_win_main_menu_get(Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return NULL;

#ifdef HAVE_ELEMENTARY_X
   Eina_Bool use_dbus = EINA_FALSE;
#endif

   if (sd->main_menu) goto end;

   sd->main_menu = elm_menu_add(obj);
   _elm_menu_menu_bar_set(sd->main_menu, EINA_TRUE);

#ifdef HAVE_ELEMENTARY_X
   if (!_elm_config->disable_external_menu && sd->x.xwin) use_dbus = EINA_TRUE;
#endif

#ifdef HAVE_ELEMENTARY_X
   if (use_dbus && _elm_dbus_menu_register(sd->main_menu))
     {
        _internal_elm_win_xwindow_get(sd);
        _elm_dbus_menu_app_menu_register(sd->x.xwin, sd->main_menu,
                                         _dbus_menu_set, obj);
     }
   else
#endif
     _dbus_menu_set(EINA_FALSE, obj);

end:
   return sd->main_menu;
}

EAPI void
elm_win_aspect_set(Eo *obj, double aspect)
{
   Eina_Size2D sz = { 0, 0 };

   if (aspect > DBL_EPSILON)
     sz = EINA_SIZE2D(1000 * aspect, 1000);

   efl_gfx_hint_aspect_set(obj, EFL_GFX_HINT_ASPECT_NONE, sz);
}

EAPI double
elm_win_aspect_get(const Eo *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return 0.0;
   return _win_aspect_get(sd);
}

/* legacy APIs */

static void
_fake_canvas_set(Evas_Object *obj, Ecore_Evas *oee)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   if (!sd) return;

   sd->ee = oee;
   _elm_win_need_frame_adjust(sd, ecore_evas_engine_name_get(oee));
}

/**
  * @internal
  *
  * Recalculate the size of window considering its resize objects' weight and
  * min size. If any of its resize objects' weight equals to 0.0, window
  * layout's weight will be set to 0.0.
  *
  * @param o box object
  * @param p box's private data
  * @param data window object
  */
static void
_window_layout_stack(Evas_Object *o, Evas_Object_Box_Data *p, void *data)
{
   const Eina_List *l;
   Evas_Object *child;
   Evas_Object_Box_Option *opt;
   Evas_Coord x, y, w, h, menuw = 0;
   double wx, wy;
   Evas_Coord minw = -1, minh = -1;
   double weight_x = EVAS_HINT_EXPAND;
   double weight_y = EVAS_HINT_EXPAND;

   ELM_WIN_DATA_GET(data, sd);
   if (sd->main_menu && efl_gfx_entity_visible_get(sd->main_menu))
     evas_object_size_hint_combined_min_get(sd->main_menu, &menuw, NULL);

   EINA_LIST_FOREACH(p->children, l, opt)
     {
        child = opt->obj;
        efl_gfx_hint_weight_get(child, &wx, &wy);
        if (EINA_DBL_EQ(wx, 0.0)) weight_x = 0;
        if (EINA_DBL_EQ(wy, 0.0)) weight_y = 0;

        evas_object_size_hint_combined_min_get(child, &w, &h);
        if (w > minw) minw = w;
        if (h > minh) minh = h;
     }

   if (minw < menuw) minw = menuw;
   efl_gfx_hint_size_restricted_min_set(o, EINA_SIZE2D(minw, minh));
   evas_object_geometry_get(o, &x, &y, &w, &h);
   if (w < minw) w = minw;
   if (h < minh) h = minh;
   evas_object_resize(o, w, h);

   EINA_LIST_FOREACH(p->children, l, opt)
     {
        child = opt->obj;
        evas_object_geometry_set(child, x, y, w, h);
     }

   efl_gfx_hint_weight_set(sd->legacy.edje, weight_x, weight_y);
   //evas_object_smart_changed(sd->legacy.edje);
}

static void
_elm_win_legacy_init(Efl_Ui_Win_Data *sd)
{
   sd->legacy.edje = edje_object_add(sd->evas);
   _elm_win_theme_internal(sd->obj, sd);

   sd->legacy.box = evas_object_box_add(sd->evas);
   evas_object_box_layout_set(sd->legacy.box, _window_layout_stack, sd->obj, NULL);

   if (elm_widget_is_legacy(sd->obj))
     edje_object_part_swallow(sd->legacy.edje, "elm.swallow.contents", sd->legacy.box);
   else
     edje_object_part_swallow(sd->legacy.edje, "efl.contents", sd->legacy.box);

   if (sd->type != EFL_UI_WIN_TYPE_FAKE)
     {
        edje_object_update_hints_set(sd->legacy.edje, EINA_TRUE);
        evas_object_event_callback_add(sd->legacy.edje, EVAS_CALLBACK_CHANGED_SIZE_HINTS,
                                       _elm_win_on_resize_obj_changed_size_hints, sd->obj);
     }
}

EAPI void
elm_win_resize_object_add(Eo *obj, Evas_Object *subobj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   Eina_Bool ret;

   if (!sd) return;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return;
     }

   // Little hack for E
   if (evas_obj_box_count(sd->legacy.box) > 0)
     sd->single_edje_content = 0;
   else if (efl_isa(subobj, EFL_CANVAS_LAYOUT_CLASS))
     sd->single_edje_content = 1;

   ret  = elm_widget_sub_object_add(obj, subobj);
   ret &= (evas_object_box_append(sd->legacy.box, subobj) != NULL);

   if (!ret)
     ERR("could not add sub object %p to window %p", subobj, obj);
}

EAPI void
elm_win_resize_object_del(Eo *obj, Evas_Object *subobj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   Eina_Bool ret;

   if (!sd) return;
   if (sd->legacy.forbidden)
     {
        CRI("Use of this API is forbidden after calling an EO API on this "
            "window. Fix your code!");
        return;
     }

   ret  = elm_widget_sub_object_del(obj, subobj);
   ret &= evas_object_box_remove(sd->legacy.box, subobj);

   if (!ret)
     ERR("could not remove sub object %p from window %p", subobj, obj);
}

EAPI Eina_Bool
elm_win_keygrab_set(Elm_Win *obj, const char *key,
                    Evas_Modifier_Mask modifiers EINA_UNUSED,
                    Evas_Modifier_Mask not_modifiers EINA_UNUSED,
                    int priority EINA_UNUSED, Elm_Win_Keygrab_Mode grab_mode)
{
   // Note: Not converting modifiers as they are not used in the implementation
   Eina_Bool ret = EINA_FALSE;
#ifdef HAVE_ELEMENTARY_X
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        Ecore_X_Win_Keygrab_Mode x_grab_mode;
        switch (grab_mode)
          {
           case ELM_WIN_KEYGRAB_SHARED:
             x_grab_mode = ECORE_X_WIN_KEYGRAB_SHARED;
             break;
           case ELM_WIN_KEYGRAB_TOPMOST:
             x_grab_mode = ECORE_X_WIN_KEYGRAB_TOPMOST;
             break;
           case ELM_WIN_KEYGRAB_EXCLUSIVE:
             x_grab_mode = ECORE_X_WIN_KEYGRAB_EXCLUSIVE;
             break;
           case ELM_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE:
             x_grab_mode = ECORE_X_WIN_KEYGRAB_OVERRIDE_EXCLUSIVE;
             break;
           default:
             return ret;
          }
         ret = ecore_x_window_keygrab_set(sd->x.xwin, key, 0, 0, 0, x_grab_mode);
     }
#else
   (void)obj;
   (void)key;
   (void)grab_mode;
#endif
   return ret;
}

EAPI Eina_Bool
elm_win_keygrab_unset(Elm_Win *obj, const char *key,
                      Evas_Modifier_Mask modifiers EINA_UNUSED,
                      Evas_Modifier_Mask not_modifiers EINA_UNUSED)
{
   // Note: Not converting modifiers as they are not used in the implementation
   Eina_Bool ret = EINA_FALSE;
#ifdef HAVE_ELEMENTARY_X
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
   _internal_elm_win_xwindow_get(sd);
   if (sd->x.xwin)
     {
        _internal_elm_win_xwindow_get(sd);
        ret = ecore_x_window_keygrab_unset(sd->x.xwin, key, 0, 0);
     }
#else
   (void)obj;
   (void)key;
#endif
   return ret;
}

EAPI Eina_Bool
elm_win_socket_listen(Efl_Ui_Win *obj, const char *svcname, int svcnum, Eina_Bool svcsys)
{
   return efl_ui_win_socket_listen(obj, svcname, svcnum, svcsys);
}

EAPI Eina_Bool
elm_win_focus_get(const Efl_Ui_Win *obj)
{
   EINA_SAFETY_ON_FALSE_RETURN_VAL(efl_isa(obj, MY_CLASS), EINA_FALSE);
   return efl_ui_focus_object_focus_get(obj);
}

EAPI Eina_Bool
elm_win_available_profiles_get(const Elm_Win *obj, char ***profiles, unsigned int *count)
{
   const Eina_Array *ar;

   if (!efl_isa(obj, MY_CLASS)) return EINA_FALSE;
   ar = efl_ui_win_wm_available_profiles_get(obj);
   if (!ar)
     {
        if (profiles) *profiles = NULL;
        if (count) *count = 0;
        return EINA_FALSE;
     }

   if (profiles) *profiles = (char **) ar->data;
   if (count) *count = ar->count;
   return EINA_TRUE;
}

EAPI void
elm_win_available_profiles_set(Elm_Win *obj, const char **profiles, unsigned int count)
{
   if (!efl_isa(obj, MY_CLASS)) return;
   if ((count > 0) && (profiles))
     {
        Eina_Array *ar;
        unsigned int i;

        ar = eina_array_new(count);
        if (ar)
          {
             for (i = 0; i < count; i++)
               eina_array_push(ar, profiles[i]);
             efl_ui_win_wm_available_profiles_set(obj, ar);
             eina_array_free(ar);
          }
     }
   else efl_ui_win_wm_available_profiles_set(obj, NULL);
}

// deprecated

EAPI void
elm_win_fake_canvas_set(Evas_Object *obj EINA_UNUSED, Ecore_Evas *oee EINA_UNUSED)
{
   ERR("Calling deprecrated function '%s'", __func__);
}

EAPI void
elm_win_name_set(Evas_Object *obj, const char *name)
{
   ERR("Calling deprecrated function '%s'", __func__);
   efl_ui_win_name_set(obj, name);
}

EAPI void
elm_win_type_set(Evas_Object *obj, Elm_Win_Type type)
{
   ERR("Calling deprecrated function '%s'", __func__);
   efl_ui_win_type_set(obj, _elm_win_type_to_efl_ui_win_type(type));
}

EAPI void
elm_win_teamwork_uri_preload(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED)
{
   ERR("Calling deprecrated function '%s'", __func__);
}

EAPI void
elm_win_teamwork_uri_show(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED)
{
   ERR("Calling deprecrated function '%s'", __func__);
}

EAPI void
elm_win_teamwork_uri_hide(Efl_Ui_Win *obj EINA_UNUSED)
{
   ERR("Calling deprecrated function '%s'", __func__);
}

EAPI void
elm_win_teamwork_uri_open(Efl_Ui_Win *obj EINA_UNUSED, const char *uri EINA_UNUSED)
{
   ERR("Calling deprecrated function '%s'", __func__);
}

/* What here follows is code that implements the glue between ecore evas and efl_ui* side */
typedef struct {
   Eo *obj;
   Eina_Bool currently_inside;
} Ui_Dnd_Target;

static inline Efl_Ui_Cnp_Buffer
_ui_buffer_get(Ecore_Evas_Selection_Buffer buffer)
{
   if (buffer == ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER)
     return EFL_UI_CNP_BUFFER_SELECTION;
   else if (buffer == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER)
     return EFL_UI_CNP_BUFFER_COPY_AND_PASTE;

   return -1;
}

void
_register_selection_changed(Efl_Ui_Selection *selection)
{
   if (!selection) return;
   ELM_WIN_DATA_GET(efl_provider_find(selection, EFL_UI_WIN_CLASS), pd);

   eina_array_push(pd->planned_changes, selection);
}

static Eina_Bool
_remove_object(void *data, void *gdata)
{
   if (data == gdata)
     return EINA_FALSE;
   return EINA_TRUE;
}

static void
_selection_changed_cb(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection)
{
   Efl_Ui_Win_Data *pd = _elm_win_associate_get(ee);
   Efl_Ui_Wm_Selection_Changed changed = {
      .seat = seat,
      .buffer = _ui_buffer_get(selection),
      .caused_by = eina_array_count(pd->planned_changes) > 0 ? eina_array_data_get(pd->planned_changes, 0) : NULL,
   };

   for (unsigned int i = 0; i < eina_array_count(pd->selection_changed); ++i)
     {
        Eo *obj = eina_array_data_get(pd->selection_changed, i);

        efl_event_callback_call(obj, EFL_UI_SELECTION_EVENT_WM_SELECTION_CHANGED, &changed);
     }

   if (changed.caused_by)
     eina_array_remove(pd->planned_changes, _remove_object, changed.caused_by);
}

static void
_motion_cb(Ecore_Evas *ee, unsigned int seat, Eina_Position2D p)
{
   Efl_Ui_Win_Data *pd = _elm_win_associate_get(ee);
   for (unsigned int i = 0; i < eina_inarray_count(pd->drop_target); ++i)
     {
        Ui_Dnd_Target *target = eina_inarray_nth(pd->drop_target, i);
        Eina_Rect rect = efl_gfx_entity_geometry_get(target->obj);
        Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, p.x, p.y);
        Efl_Ui_Drop_Event ev = {p, seat, ecore_evas_drop_available_types_get(ee, seat)};

        if (target->currently_inside && !inside)
          {
             target->currently_inside = EINA_FALSE;
             efl_event_callback_call(target->obj, EFL_UI_DND_EVENT_DROP_LEFT, &ev);
             ecore_evas_dnd_mark_motion_used(ee, seat);
          }
        else if (!target->currently_inside && inside)
          {
             target->currently_inside = EINA_TRUE;
             efl_event_callback_call(target->obj, EFL_UI_DND_EVENT_DROP_ENTERED, &ev);
             ecore_evas_dnd_mark_motion_used(ee, seat);
          }
        else if (target->currently_inside && inside)
          {
             efl_event_callback_call(target->obj, EFL_UI_DND_EVENT_DROP_POSITION_CHANGED, &ev);
             ecore_evas_dnd_mark_motion_used(ee, seat);
          }
        eina_accessor_free(ev.available_types);
     }
}

static void
_enter_state_change_cb(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Position2D p, Eina_Bool move_inside)
{
   Efl_Ui_Win_Data *pd = _elm_win_associate_get(ee);
   for (unsigned int i = 0; i < eina_inarray_count(pd->drop_target); ++i)
     {
        Ui_Dnd_Target *target = eina_inarray_nth(pd->drop_target, i);
        Eina_Rect rect = efl_gfx_entity_geometry_get(target->obj);
        Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, p.x, p.y);
        Efl_Ui_Drop_Event ev = {p, seat, ecore_evas_drop_available_types_get(ee, seat)};

        if (inside && move_inside)
          {
             target->currently_inside = EINA_TRUE;
             efl_event_callback_call(target->obj, EFL_UI_DND_EVENT_DROP_ENTERED, &ev);
          }
        else if (!move_inside && target->currently_inside)
          {
             target->currently_inside = EINA_FALSE;
             efl_event_callback_call(target->obj, EFL_UI_DND_EVENT_DROP_LEFT, &ev);
          }
     }
}

static void
_drop_cb(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Position2D p, const char *action)
{
   Eina_List *itr, *top_objects_list = NULL;
   Efl_Ui_Win_Data *pd = _elm_win_associate_get(ee);
   Eina_Array *tmp = eina_array_new(10);
   Eo *top_obj;

   for (unsigned int i = 0; i < eina_inarray_count(pd->drop_target); ++i)
     {
        Ui_Dnd_Target *target = eina_inarray_nth(pd->drop_target, i);
        Eina_Rect rect = efl_gfx_entity_geometry_get(target->obj);
        Eina_Bool inside = eina_rectangle_coords_inside(&rect.rect, p.x, p.y);

        if (inside)
          {
             EINA_SAFETY_ON_FALSE_GOTO(target->currently_inside, end);
             eina_array_push(tmp, target->obj);
          }
     }

   /* We retrieve the (non-smart) objects pointed by (px, py) */
   top_objects_list = evas_tree_objects_at_xy_get(ecore_evas_get(ee), NULL, p.x, p.y);
   /* We walk on this list from the last because if the list contains more than one
    * element, all but the last will repeat events. The last one can repeat events
    * or not. Anyway, this last one is the first that has to be taken into account
    * for the determination of the drop target.
    */
   EINA_LIST_REVERSE_FOREACH(top_objects_list, itr, top_obj)
     {
        Evas_Object *object = top_obj;
        /* We search for the dropable data into the object. If not found, we search into its parent.
         * For example, if a button is a drop target, the first object will be an (internal) image.
         * The drop target is attached to the button, i.e to image's parent. That's why we need to
         * walk on the parents until NULL.
         * If we find this dropable data, we found our drop target.
         */
        while (object)
          {
             unsigned int out_idx;
             if (!eina_array_find(tmp, object, &out_idx))
               {
                  object = evas_object_smart_parent_get(object);
               }
             else
               {
                  Efl_Ui_Drop_Dropped_Event ev = {{p, seat, ecore_evas_drop_available_types_get(ee, seat)}, action};
                  efl_event_callback_call(object, EFL_UI_DND_EVENT_DROP_DROPPED, &ev);
                  goto end;
               }
          }
     }
end:
   eina_list_free(top_objects_list);
   eina_array_free(tmp);
}

static void
_ee_backbone_init(Efl_Ui_Win *obj EINA_UNUSED, Efl_Ui_Win_Data *pd)
{
   pd->selection_changed = eina_array_new(1);
   pd->drop_target = eina_inarray_new(sizeof(Ui_Dnd_Target), 1);

   ecore_evas_callback_selection_changed_set(pd->ee, _selection_changed_cb);
   ecore_evas_callback_drop_drop_set(pd->ee, _drop_cb);
   ecore_evas_callback_drop_motion_set(pd->ee, _motion_cb);
   ecore_evas_callback_drop_state_changed_set(pd->ee, _enter_state_change_cb);
}

static void
_ee_backbone_shutdown(Efl_Ui_Win *obj EINA_UNUSED, Efl_Ui_Win_Data *pd)
{
   ecore_evas_callback_selection_changed_set(pd->ee, NULL);
   ecore_evas_callback_drop_drop_set(pd->ee, NULL);
   ecore_evas_callback_drop_motion_set(pd->ee, NULL);
   ecore_evas_callback_drop_state_changed_set(pd->ee, NULL);

   eina_array_free(pd->selection_changed);
   pd->selection_changed = NULL;
   eina_inarray_free(pd->drop_target);
   pd->drop_target = NULL;

}

static Eina_Bool
_remove(void *data, void *gdata)
{
   if (data == gdata)
     return EINA_FALSE;
   return EINA_TRUE;
}

Efl_Ui_Win*
efl_ui_win_get(Evas_Object *obj)
{
   Efl_Ui_Win *win = efl_provider_find(obj, MY_CLASS);
   if (!win)
     {
        Evas *e = evas_object_evas_get(obj);
        Ecore_Evas *ee = ecore_evas_ecore_evas_get(e);

        win = ecore_evas_data_get(ee, "elm_win");
     }
   EINA_SAFETY_ON_NULL_RETURN_VAL(win, NULL);
   return win;
}

static Efl_Ui_Win_Data*
_fetch_win_data_from_arbitary_obj(Efl_Canvas_Object *obj)
{
   Efl_Ui_Win *win = efl_ui_win_get(obj);
   EINA_SAFETY_ON_NULL_RETURN_VAL(win, NULL);
   Efl_Ui_Win_Data *pd = efl_data_scope_safe_get(win, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);
   return pd;
}

void
_drop_event_register(Eo *obj)
{
   Ui_Dnd_Target target = {obj, EINA_FALSE};
   Efl_Ui_Win_Data *pd = _fetch_win_data_from_arbitary_obj(obj);
   if (!pd) return;

   eina_inarray_push(pd->drop_target, &target);
}

void
_drop_event_unregister(Eo *obj)
{
   int idx = -1;

   Efl_Ui_Win_Data *pd = _fetch_win_data_from_arbitary_obj(obj);
   if (!pd) return;

   for (unsigned int i = 0; i < eina_inarray_count(pd->drop_target); ++i)
     {
        Ui_Dnd_Target *target = eina_inarray_nth(pd->drop_target, i);
        if (target->obj == obj)
          {
             //FIXME emit drop
             target->currently_inside = EINA_FALSE;
             idx = i;
          }
     }
   if (idx != -1)
     eina_inarray_remove_at(pd->drop_target, idx);
}

void
_selection_changed_event_register(Eo *obj)
{
   Efl_Ui_Win_Data *pd = _fetch_win_data_from_arbitary_obj(obj);
   if (!pd) return;

   eina_array_push(pd->selection_changed, obj);
}
void
_selection_changed_event_unregister(Eo *obj)
{
   Efl_Ui_Win_Data *pd = _fetch_win_data_from_arbitary_obj(obj);
   if (!pd) return;

   eina_array_remove(pd->selection_changed, _remove, obj);
}
/* Internal EO APIs and hidden overrides */

ELM_WIDGET_KEY_DOWN_DEFAULT_IMPLEMENT(efl_ui_win, Efl_Ui_Win_Data)

/* Internal EO APIs and hidden overrides */

#define EFL_UI_WIN_EXTRA_OPS \
   EFL_CANVAS_GROUP_ADD_DEL_OPS(efl_ui_win), \
   ELM_PART_CONTENT_DEFAULT_OPS(efl_ui_win), \
   EFL_OBJECT_OP_FUNC(efl_canvas_object_legacy_ctor, _efl_ui_win_efl_canvas_object_legacy_ctor)

#include "efl_ui_win.eo.c"

static void
_efl_ui_win_legacy_class_constructor(Efl_Class *klass)
{
   evas_smart_legacy_type_register(MY_CLASS_NAME_LEGACY, klass);
}

EOLIAN static Eo *
_efl_ui_win_legacy_efl_object_finalize(Eo *obj, void *pd EINA_UNUSED)
{
   obj = efl_finalize(efl_super(obj, EFL_UI_WIN_LEGACY_CLASS));
   efl_canvas_object_type_set(obj, MY_CLASS_NAME_LEGACY);
   return obj;
}


Efl_Object *_efl_ui_win_legacy_efl_object_finalize(Eo *obj, void *pd);


static Eina_Bool
_efl_ui_win_legacy_class_initializer(Efl_Class *klass)
{
   const Efl_Object_Ops *opsp = NULL;

   const Efl_Object_Property_Reflection_Ops *ropsp = NULL;

#ifndef EFL_UI_WIN_LEGACY_EXTRA_OPS
#define EFL_UI_WIN_LEGACY_EXTRA_OPS
#endif

   EFL_OPS_DEFINE(ops,
      EFL_OBJECT_OP_FUNC(efl_finalize, _efl_ui_win_legacy_efl_object_finalize),
      EFL_UI_WIN_LEGACY_EXTRA_OPS
   );
   opsp = &ops;

   return efl_class_functions_set(klass, opsp, ropsp);
}

static const Efl_Class_Description _efl_ui_win_legacy_class_desc = {
   EO_VERSION,
   "Efl.Ui.Win_Legacy",
   EFL_CLASS_TYPE_REGULAR,
   0,
   _efl_ui_win_legacy_class_initializer,
   _efl_ui_win_legacy_class_constructor,
   NULL
};

EFL_DEFINE_CLASS(efl_ui_win_legacy_class_get, &_efl_ui_win_legacy_class_desc, EFL_UI_WIN_CLASS, EFL_UI_LEGACY_INTERFACE, NULL);


EAPI Evas_Object *
elm_win_add(Evas_Object *parent, const char *name, Elm_Win_Type type)
{
   const Efl_Class *klass = EFL_UI_WIN_LEGACY_CLASS;

   switch ((int) type)
     {
      case ELM_WIN_INLINED_IMAGE:
        klass = EFL_UI_WIN_INLINED_LEGACY_CLASS;
        break;
      case ELM_WIN_SOCKET_IMAGE:
        klass = EFL_UI_WIN_SOCKET_LEGACY_CLASS;
        break;
      default:
        break;
     }

   return elm_legacy_add(klass, parent,
                         efl_ui_win_name_set(efl_added, name),
                         efl_ui_win_type_set(efl_added, (Efl_Ui_Win_Type)type));
}


EAPI Evas_Object *
elm_win_fake_add(Ecore_Evas *ee)
{
   return elm_legacy_add(EFL_UI_WIN_LEGACY_CLASS, NULL,
                        _fake_canvas_set(efl_added, ee),
                        efl_ui_win_name_set(efl_added, NULL),
                        efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_FAKE));
}

EAPI Evas_Object *
elm_win_util_standard_add(const char *name, const char *title)
{
   Evas_Object *win;

   win = elm_legacy_add(EFL_UI_WIN_LEGACY_CLASS, NULL,
                        efl_text_set(efl_added, title),
                        efl_ui_win_name_set(efl_added, name),
                        efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_BASIC));
   if (!win) return NULL;

   _elm_win_standard_init(win);
   return win;
}

EAPI Evas_Object *
elm_win_util_dialog_add(Evas_Object *parent, const char *name, const char *title)
{
   Evas_Object *win;

   win = elm_legacy_add(EFL_UI_WIN_LEGACY_CLASS, parent,
                        efl_text_set(efl_added, title),
                        efl_ui_win_name_set(efl_added, name),
                        efl_ui_win_type_set(efl_added, EFL_UI_WIN_TYPE_DIALOG_BASIC));
   if (!win) return NULL;

   _elm_win_standard_init(win);
   return win;
}


EAPI void
elm_win_keyboard_mode_set(Evas_Object *obj, Elm_Win_Keyboard_Mode mode)
{
   efl_ui_win_keyboard_mode_set(obj, (Efl_Ui_Win_Keyboard_Mode)mode);
}

EAPI Elm_Win_Keyboard_Mode
elm_win_keyboard_mode_get(const Evas_Object *obj)
{
   return (Elm_Win_Keyboard_Mode)efl_ui_win_keyboard_mode_get(obj);
}

EAPI void
elm_win_screen_constrain_set(Evas_Object *obj, Eina_Bool constrain)
{
   efl_ui_win_screen_constrain_set(obj, constrain);
}

EAPI Eina_Bool
elm_win_screen_constrain_get(const Evas_Object *obj)
{
   return efl_ui_win_screen_constrain_get(obj);
}

EAPI void
elm_win_prop_focus_skip_set(Evas_Object *obj, Eina_Bool skip)
{
   efl_ui_win_prop_focus_skip_set(obj, skip);
}

EAPI void
elm_win_autohide_set(Evas_Object *obj, Eina_Bool autohide)
{
   efl_ui_win_autohide_set(obj, autohide);
}

EAPI Eina_Bool
elm_win_autohide_get(const Evas_Object *obj)
{
   return efl_ui_win_autohide_get(obj);
}

EAPI void
elm_win_exit_on_close_set(Evas_Object *obj, const Eina_Value *exit_code)
{
   efl_ui_win_exit_on_close_set(obj, exit_code);
}

EAPI const Eina_Value *
elm_win_exit_on_close_get(const Evas_Object *obj)
{
   return efl_ui_win_exit_on_close_get(obj);
}

EAPI void
elm_win_icon_object_set(Evas_Object *obj, Evas_Object *icon)
{
   efl_ui_win_icon_object_set(obj, icon);
}

EAPI const Evas_Object *
elm_win_icon_object_get(const Evas_Object *obj)
{
   return efl_ui_win_icon_object_get(obj);
}

EAPI void
elm_win_iconified_set(Evas_Object *obj, Eina_Bool iconified)
{
   efl_ui_win_minimized_set(obj, iconified);
}

EAPI Eina_Bool
elm_win_iconified_get(const Evas_Object *obj)
{
   return efl_ui_win_minimized_get(obj);
}

EAPI void
elm_win_maximized_set(Evas_Object *obj, Eina_Bool maximized)
{
   efl_ui_win_maximized_set(obj, maximized);
}

EAPI Eina_Bool
elm_win_maximized_get(const Evas_Object *obj)
{
   return efl_ui_win_maximized_get(obj);
}

EAPI void
elm_win_fullscreen_set(Evas_Object *obj, Eina_Bool fullscreen)
{
   efl_ui_win_fullscreen_set(obj, fullscreen);
}

EAPI Eina_Bool
elm_win_fullscreen_get(const Evas_Object *obj)
{
   return efl_ui_win_fullscreen_get(obj);
}

EAPI void
elm_win_sticky_set(Evas_Object *obj, Eina_Bool sticky)
{
   efl_ui_win_sticky_set(obj, sticky);
}

EAPI Eina_Bool
elm_win_sticky_get(const Evas_Object *obj)
{
   return efl_ui_win_sticky_get(obj);
}

EAPI void
elm_win_noblank_set(Evas_Object *obj, Eina_Bool noblank)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN(sd);
   noblank = !!noblank;
   if (sd->noblank == noblank) return;
   sd->noblank = noblank;
   _win_noblank_eval();
}

EAPI Eina_Bool
elm_win_noblank_get(const Evas_Object *obj)
{
   Efl_Ui_Win_Data *sd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(sd, EINA_FALSE);
   return sd->noblank;
}

EAPI void
elm_win_borderless_set(Evas_Object *obj, Eina_Bool borderless)
{
   efl_ui_win_borderless_set(obj, borderless);
}

EAPI Eina_Bool
elm_win_borderless_get(const Evas_Object *obj)
{
   return efl_ui_win_borderless_get(obj);
}

EAPI void
elm_win_role_set(Evas_Object *obj, const char *role)
{
   efl_ui_win_role_set(obj, role);
}

EAPI const char *
elm_win_role_get(const Evas_Object *obj)
{
   return efl_ui_win_role_get(obj);
}

EAPI const char *
elm_win_name_get(const Evas_Object *obj)
{
   return efl_ui_win_name_get(obj);
}

EAPI Elm_Win_Type
elm_win_type_get(const Evas_Object *obj)
{
   if (!(efl_isa(obj, EFL_UI_WIN_CLASS) ||
         efl_isa(obj, EFL_UI_WIN_LEGACY_CLASS) ||
         efl_isa(obj, EFL_UI_WIN_INLINED_CLASS))) return ELM_WIN_UNKNOWN;

   return _efl_ui_win_type_to_elm_win_type(efl_ui_win_type_get(obj));
}

EAPI const char *
elm_win_accel_preference_get(const Evas_Object *obj)
{
   return efl_ui_win_accel_preference_get(obj);
}

EAPI void
elm_win_alpha_set(Evas_Object *obj, Eina_Bool alpha)
{
   efl_ui_win_alpha_set(obj, alpha);
}

EAPI Eina_Bool
elm_win_alpha_get(const Evas_Object *obj)
{
   return efl_ui_win_alpha_get(obj);
}

EAPI void
elm_win_activate(Evas_Object *obj)
{
   efl_ui_win_activate(obj);
}

EAPI void
elm_win_center(Evas_Object *obj, Eina_Bool h, Eina_Bool v)
{
   efl_ui_win_center(obj, h, v);
}

EAPI Eina_Bool
elm_win_move_resize_start(Evas_Object *obj, Elm_Win_Move_Resize_Mode mode)
{
   return efl_ui_win_move_resize_start(obj, (Efl_Ui_Win_Move_Resize_Mode)mode);
}

EAPI void
elm_win_focus_highlight_animate_set(Efl_Ui_Win *obj, Eina_Bool animate)
{
   efl_ui_win_focus_highlight_animate_set(obj, animate);
}

EAPI Eina_Bool
elm_win_focus_highlight_animate_get(const Efl_Ui_Win *obj)
{
   return efl_ui_win_focus_highlight_animate_get(obj);
}

EAPI void
elm_win_focus_highlight_enabled_set(Efl_Ui_Win *obj, Eina_Bool enabled)
{
   efl_ui_win_focus_highlight_enabled_set(obj, enabled);
}

EAPI Eina_Bool
elm_win_focus_highlight_enabled_get(const Efl_Ui_Win *obj)
{
   return efl_ui_win_focus_highlight_enabled_get(obj);
}

EAPI Eina_Bool
elm_win_focus_highlight_style_set(Efl_Ui_Win *obj, const char *style)
{
   return efl_ui_win_focus_highlight_style_set(obj, style);
}

EAPI const char *
elm_win_focus_highlight_style_get(const Efl_Ui_Win *obj)
{
   return efl_ui_win_focus_highlight_style_get(obj);
}

EAPI Efl_Ui_Shared_Win_Data*
efl_ui_win_shared_data_get(Efl_Ui_Win *obj)
{
   Efl_Ui_Win_Data *pd = efl_data_scope_safe_get(obj, MY_CLASS);
   EINA_SAFETY_ON_NULL_RETURN_VAL(pd, NULL);

   return &pd->spd;
}

EAPI void
efl_ui_win_autodel_set(Efl_Ui_Win *obj, Eina_Bool autodel)
{
   elm_win_autodel_set(obj, autodel);
}

EAPI Eina_Bool
efl_ui_win_autodel_get(const Efl_Ui_Win *obj)
{
   return elm_win_autodel_get(obj);
}
