#include "e.h"

/* data keys:

   = keys that return objects =
   - E_Client: the client associated with the object (E_Client*)
   - comp_smart_obj: cw->smart_obj (Evas_Object*)
   - comp_obj: cw (E_Comp_Object*)

   = keys that are bool flags =
   - client_restack: client needs a protocol-level restack
   - comp_override: object is triggering a nocomp override to force compositing
   - comp_ref: object has a ref from visibility animations
   - comp_showing: object is currently running its show animation
   - comp_hiding: object is currently running its hiding animation
   - comp_object: object is a compositor-created object
   - comp_object_skip: object has a name which prohibits theme shadows
   - comp_object-to_del: list of objects which will be deleted when this object is deleted
   - comp_mirror: object is the result of e_comp_object_util_mirror_add()
*/

#define UPDATE_MAX 512 // same as evas
#define FAILURE_MAX 2 // seems reasonable
#define SMART_NAME     "e_comp_object"

/* for non-util functions */
#define API_ENTRY      E_Comp_Object *cw; \
                       cw = evas_object_smart_data_get(obj); \
                       if ((!obj) || (!cw) || (e_util_strcmp(evas_object_type_get(obj), SMART_NAME))) return

/* for util functions (obj may or may not be E_Comp_Object */
#define SOFT_ENTRY(...)      E_Comp_Object *cw; \
                       if (!obj) \
                         { \
                            CRI("YOU PASSED NULL! ARGH!"); \
                            return __VA_ARGS__; \
                         } \
                       cw = evas_object_smart_data_get(obj); \
                       if ((!cw) || (e_util_strcmp(evas_object_type_get(obj), SMART_NAME))) \
                         cw = NULL
#define INTERNAL_ENTRY E_Comp_Object *cw; cw = evas_object_smart_data_get(obj);

/* enable for lots of client size info in console output */
#if 1
# define e_util_size_debug_set(x, y)
#endif

/* enable along with display-specific damage INF calls to enable render tracing
 * SLOW!
 */
static int render_debug_enabled;
#define RENDER_DEBUG(...) do { if ((render_debug_enabled == 1) || ((render_debug_enabled == -1) && cw->ec->focused)) INF(__VA_ARGS__); } while (0)

typedef struct _E_Comp_Object
{
   //EINA_INLIST;

   int                  x, y, w, h;  // geometry
   Eina_Tiler          *input_area;

   E_Client *ec;

   E_Comp_Object_Frame client_inset;
   struct {
      double          start;
      double          val;
      int             x, y;
      E_Direction     dir;
      Ecore_Animator *anim;
   } shade;
   struct {
      int             bx, by, bxx, byy, w, h;
   } border;

   Eina_Stringshare   *frame_theme;
   Eina_Stringshare   *frame_name;
   Eina_Stringshare   *visibility_effect; //effect when toggling visibility

   Evas_Object         *smart_obj;  // smart object
   Evas_Object         *clip; // clipper over effect object
   Eina_Array          *input_objs; // input rect
   Evas_Object         *obj;  // composite object
   Evas_Object         *frame_object; // for client frames
   Evas_Object         *frame_icon; // for client frames
   Evas_Object         *zoomobj; // zoomap
   Evas_Object         *shobj;  // shadow object
   Evas_Object         *effect_obj; // effects object
   Evas_Object         *frame_volume; // volume level object
   unsigned int         layer; //e_comp_canvas_layer_map(cw->ec->layer)
   Eina_List           *obj_mirror;  // extra mirror objects
   Eina_List           *obj_agent;  // extra agent objects
   Eina_Tiler          *updates; //render update regions
   Eina_Tiler          *pending_updates; //render update regions which are about to render

   Evas_Native_Surface *ns; //for custom gl rendering

   double               action_client_loop_time; //loop time when client's action ended

   unsigned int         update_count;  // how many updates have happened to this obj

   unsigned int         opacity;  // opacity set with _NET_WM_WINDOW_OPACITY

   unsigned int         animating;  // it's busy animating
   unsigned int         failures; //number of consecutive e_pixmap_image_draw() failures
   unsigned int         force_visible; //number of visible obj_mirror objects
   Eina_Bool            deleted E_BITFIELD;  // deleted
   Eina_Bool            defer_hide E_BITFIELD;  // flag to get hide to work on deferred hide
   Eina_Bool            showing E_BITFIELD;  // object is currently in "show" animation
   Eina_Bool            visible E_BITFIELD;  // is visible

   Eina_Bool            shaped E_BITFIELD;  // is shaped
   Eina_Bool            update E_BITFIELD;  // has updates to fetch
   Eina_Bool            redirected E_BITFIELD;  // has updates to fetch
   Eina_Bool            native E_BITFIELD;  // native

   Eina_Bool            nocomp E_BITFIELD;  // nocomp applied
   Eina_Bool            nocomp_need_update E_BITFIELD;  // nocomp in effect, but this window updated while in nocomp mode
   Eina_Bool            real_hid E_BITFIELD;  // last hide was a real window unmap

   Eina_Bool            effect_set E_BITFIELD; //effect_obj has a valid group
   Eina_Bool            effect_running E_BITFIELD; //effect_obj is playing an animation
   Eina_Bool            effect_clip E_BITFIELD; //effect_obj is clipped
   Eina_Bool            effect_clip_able E_BITFIELD; //effect_obj will be clipped for effects

   Eina_Bool            zoomap_disabled E_BITFIELD; //whether zoomap is usable
   Eina_Bool            updates_exist E_BITFIELD;
   Eina_Bool            updates_full E_BITFIELD; // entire object will be updated

   Eina_Bool            force_move E_BITFIELD;
   Eina_Bool            frame_extends E_BITFIELD; //frame may extend beyond object size
   Eina_Bool            blanked E_BITFIELD; //window is rendering blank content (externally composited)

   Eina_Bool            agent_updating E_BITFIELD; //updating agents
} E_Comp_Object;


struct E_Comp_Object_Mover
{
   EINA_INLIST;
   E_Comp_Object_Mover_Cb func;
   const char *sig;
   void *data;
   int pri;
};

static Eina_Inlist *_e_comp_object_movers = NULL;
static Evas_Smart *_e_comp_smart = NULL;

/* sekrit functionzzz */
EINTERN void e_client_focused_set(E_Client *ec);

/* emitted every time a new noteworthy comp object is added */
E_API int E_EVENT_COMP_OBJECT_ADD = -1;

static void
_e_comp_object_event_free(void *d EINA_UNUSED, void *event)
{
   E_Event_Comp_Object *ev = event;
   E_Client *ec;

   ec = evas_object_data_get(ev->comp_object, "E_Client");
   if (ec)
     {
        UNREFD(ec, 1);
        e_object_unref(E_OBJECT(ec));
     }
   evas_object_unref(ev->comp_object);
   free(ev);
}

static void
_e_comp_object_event_add(Evas_Object *obj)
{
   E_Event_Comp_Object *ev;
   E_Client *ec;

   ev = E_NEW(E_Event_Comp_Object, 1);
   evas_object_ref(obj);
   ev->comp_object = obj;
   ec = evas_object_data_get(ev->comp_object, "E_Client");
   if (ec)
     {
        REFD(ec, 1);
        e_object_ref(E_OBJECT(ec));
     }
   ecore_event_add(E_EVENT_COMP_OBJECT_ADD, ev, _e_comp_object_event_free, NULL);
}

/////////////////////////////////////

static void
_e_comp_object_cb_agent_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   cw->obj_agent = eina_list_remove(cw->obj_agent, obj);
}

static void
_e_comp_object_cb_agent_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;
   int w, h;

   if (cw->agent_updating) return;
   evas_object_geometry_get(obj, NULL, NULL, &w, &h);
   evas_object_resize(cw->smart_obj, w, h);
}

static void
_e_comp_object_cb_agent_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;
   int x, y;

   if (cw->agent_updating) return;
   evas_object_geometry_get(obj, &x, &y, NULL, NULL);
   evas_object_move(cw->smart_obj, x, y);
}

/////////////////////////////////////

static void
_e_comp_object_cb_mirror_del(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   cw->obj_mirror = eina_list_remove(cw->obj_mirror, obj);
}

static void
_e_comp_object_cb_mirror_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if ((!cw->force_visible) && (!cw->deleted) && (!e_object_is_del(E_OBJECT(cw->ec))))
     evas_object_smart_callback_call(cw->smart_obj, "visibility_force", cw->ec);
   cw->force_visible++;
   if ((!cw->native) && cw->pending_updates && (!cw->update) && cw->real_hid)
     e_comp_object_render(cw->smart_obj);
}

static void
_e_comp_object_cb_mirror_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   cw->force_visible--;
   if ((!cw->force_visible) && (!cw->deleted) && (!e_object_is_del(E_OBJECT(cw->ec))))
     evas_object_smart_callback_call(cw->smart_obj, "visibility_normal", cw->ec);
}

/////////////////////////////////////

static inline Eina_Bool
_e_comp_shaped_check(int w, int h, const Eina_Rectangle *rects, int num)
{
   if (num > 1) return EINA_TRUE;
   if ((rects[0].x == 0) && (rects[0].y == 0) &&
       ((int)rects[0].w == w) && ((int)rects[0].h == h))
     return EINA_FALSE;
   return EINA_TRUE;
}

/////////////////////////////////////

/* add a client to the layer-client list */
static void
_e_comp_object_layers_add(E_Comp_Object *cw, E_Comp_Object *above, E_Comp_Object *below, Eina_Bool prepend)
{
   E_Comp_Object *layer_cw = NULL;

   /* try to get the internal data for the layer;
    * will return NULL for fake layers (eg. wayland)
    */
   if (e_comp->layers[cw->layer].obj)
     layer_cw = evas_object_smart_data_get(e_comp->layers[cw->layer].obj);
   if (layer_cw == cw) layer_cw = NULL;
/*
   if (above)
     e_comp->layers[cw->layer].objs = eina_inlist_append_relative(e_comp->layers[cw->layer].objs, EINA_INLIST_GET(cw), EINA_INLIST_GET(cw2));
   else if (below)
     e_comp->layers[cw->layer].objs = eina_inlist_prepend_relative(e_comp->layers[cw->layer].objs, EINA_INLIST_GET(cw), EINA_INLIST_GET(cw2));
   else
     {
        if (prepend)
          e_comp->layers[cw->layer].objs = eina_inlist_prepend(e_comp->layers[cw->layer].objs, EINA_INLIST_GET(cw));
        else
          e_comp->layers[cw->layer].objs = eina_inlist_append(e_comp->layers[cw->layer].objs, EINA_INLIST_GET(cw));
     }
   e_comp->layers[cw->layer].objs_count++;
   if (!cw->ec) return;
*/
   if (above)
     {
        cw->layer = above->layer;
        e_comp->layers[above->layer].clients = eina_inlist_append_relative(e_comp->layers[above->layer].clients, EINA_INLIST_GET(cw->ec), EINA_INLIST_GET(above->ec));
        e_comp->layers[above->layer].clients_count++;
     }
   else if (below)
     {
        cw->layer = below->layer;
        e_comp->layers[below->layer].clients = eina_inlist_prepend_relative(e_comp->layers[below->layer].clients, EINA_INLIST_GET(cw->ec), EINA_INLIST_GET(below->ec));
        e_comp->layers[below->layer].clients_count++;
     }
   else
     {
        if (prepend)
          e_comp->layers[cw->layer].clients = eina_inlist_prepend(e_comp->layers[cw->layer].clients, EINA_INLIST_GET(cw->ec));
        else if (layer_cw)
          e_comp->layers[cw->layer].clients = eina_inlist_prepend_relative(e_comp->layers[cw->layer].clients, EINA_INLIST_GET(cw->ec), EINA_INLIST_GET(layer_cw->ec));
        else //this is either the layer object or a tough actin tinactin^W^W^Wfast stacking client
          e_comp->layers[cw->layer].clients = eina_inlist_append(e_comp->layers[cw->layer].clients, EINA_INLIST_GET(cw->ec));
        e_comp->layers[cw->layer].clients_count++;
     }
#ifndef E_RELEASE_BUILD
   if (layer_cw)
     {
        E_Client *below_ec = e_client_below_get(cw->ec);
        if (below_ec)
          {
             if (e_comp->layers[cw->layer].obj == below_ec->frame)
               CRI("ACK!");
          }
     }
#endif
}

static void
_e_comp_object_layers_remove(E_Comp_Object *cw)
{
   if (cw->ec && e_comp->layers[cw->layer].clients)
     {
        e_comp->layers[cw->layer].clients = eina_inlist_remove(e_comp->layers[cw->layer].clients, EINA_INLIST_GET(cw->ec));
        e_comp->layers[cw->layer].clients_count--;
     }
/*
   e_comp->layers[cw->layer].objs = eina_inlist_remove(e_comp->layers[cw->layer].objs, EINA_INLIST_GET(cw));
   e_comp->layers[cw->layer].objs_count--;
*/
}

/////////////////////////////////////
static void
_e_comp_object_updates_init(E_Comp_Object *cw)
{
   int pw, ph;

   if (cw->updates) return;
   pw = cw->ec->client.w, ph = cw->ec->client.h;
   if ((!pw) || (!ph))
     e_pixmap_size_get(cw->ec->pixmap, &pw, &ph);
   if ((!pw) || (!ph)) return;
   cw->updates = eina_tiler_new(pw, ph);
   if (cw->updates)
     eina_tiler_tile_size_set(cw->updates, 1, 1);
}


static void
_e_comp_object_alpha_set(E_Comp_Object *cw)
{
   Eina_Bool alpha;

   if (!e_pixmap_is_x(cw->ec->pixmap))
     alpha = e_pixmap_image_is_argb(cw->ec->pixmap);
   else
     alpha = cw->ec->argb;

   if (cw->blanked || cw->ns || cw->ec->shaped) alpha = EINA_TRUE;

   evas_object_image_alpha_set(cw->obj, alpha);
}

static void
_e_comp_object_shadow(E_Comp_Object *cw)
{
   if (e_client_util_shadow_state_get(cw->ec))
     edje_object_signal_emit(cw->frame_object ?: cw->shobj, "e,state,shadow,on", "e");
   else
     edje_object_signal_emit(cw->frame_object ?: cw->shobj, "e,state,shadow,off", "e");
   if (cw->frame_object)
     edje_object_signal_emit(cw->shobj, "e,state,shadow,off", "e");
   evas_object_smart_callback_call(cw->smart_obj, "shadow_change", cw->ec);
}

/* trigger e_binding from an edje signal on a client frame */
static void
_e_comp_object_cb_signal_bind(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source)
{
   E_Comp_Object *cw = data;

#ifndef HAVE_WAYLAND_ONLY
   if (e_dnd_active()) return;
#endif
   if (cw->ec->iconic || cw->ec->cur_mouse_action) return;
   if (!dblequal(cw->action_client_loop_time, ecore_loop_time_get()))
     e_bindings_signal_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(cw->ec),
                              emission, source);
}

/////////////////////////////////////

/* handle evas mouse-in events on client object */
static void
_e_comp_object_cb_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info)
{
   Evas_Event_Mouse_In *ev = event_info;
   E_Comp_Object *cw = data;

   if (e_pixmap_is_x(cw->ec->pixmap))
     {
        if (!e_comp_object_frame_allowed(obj)) return;
        if (E_INSIDE(ev->output.x, ev->output.y, cw->ec->client.x, cw->ec->client.y,
            cw->ec->client.w, cw->ec->client.h)) return;
     }
   if (e_grabinput_mouse_win_get() && (e_grabinput_mouse_win_get() != e_client_util_win_get(cw->ec)))
     return;
   e_client_mouse_in(cw->ec, ev->output.x, ev->output.y);
}

/* handle evas mouse-out events on client object */
static void
_e_comp_object_cb_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Out *ev = event_info;
   E_Comp_Object *cw = data;

   if (e_grabinput_mouse_win_get() && (e_grabinput_mouse_win_get() != e_client_util_win_get(cw->ec)))
     return;
   e_client_mouse_out(cw->ec, ev->output.x, ev->output.y);
}

/* handle evas mouse wheel events on client object */
static void
_e_comp_object_cb_mouse_wheel(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Wheel *ev = event_info;
   E_Comp_Object *cw = data;
   E_Binding_Event_Wheel ev2;

   if (!cw->ec) return;
   if (e_client_action_get()) return;
   e_bindings_evas_event_mouse_wheel_convert(ev, &ev2);
   e_client_mouse_wheel(cw->ec, &ev->output, &ev2);
}

/* handle evas mouse down events on client object */
static void
_e_comp_object_cb_mouse_down(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev = event_info;
   E_Comp_Object *cw = data;
   E_Binding_Event_Mouse_Button ev2;

   if (!cw->ec) return;
   if (e_client_action_get()) return;
   e_bindings_evas_event_mouse_button_convert(ev, &ev2);
   e_client_mouse_down(cw->ec, ev->button, &ev->output, &ev2);
}

/* handle evas mouse up events on client object */
static void
_e_comp_object_cb_mouse_up(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Down *ev = event_info;
   E_Comp_Object *cw = data;
   E_Binding_Event_Mouse_Button ev2;
   Eina_Bool acting;

   if (!cw->ec) return;
   if (e_client_action_get() && (e_client_action_get() != cw->ec)) return;
   e_bindings_evas_event_mouse_button_convert(ev, &ev2);
   acting = !!cw->ec->cur_mouse_action;
   e_client_mouse_up(cw->ec, ev->button, &ev->output, &ev2);
   if (acting && (!e_client_action_get()))
     cw->action_client_loop_time = ecore_loop_time_get();
}

/* handle evas mouse movement events on client object */
static void
_e_comp_object_cb_mouse_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Move *ev = event_info;
   E_Comp_Object *cw = data;

   if (!cw->ec) return;
   if (e_client_action_get() && (e_client_action_get() != cw->ec)) return;
   e_client_mouse_move(cw->ec, &ev->cur.output);
}
/////////////////////////////////////

/* helper function for checking compositor themes based on user-defined matches */
static Eina_Bool
_e_comp_object_shadow_client_match(const E_Client *ec, E_Comp_Match *m)
{
   if (((m->title) && (!ec->netwm.name)) ||
       ((ec->netwm.name) && (m->title) && (!e_util_glob_match(ec->netwm.name, m->title))))
     return EINA_FALSE;
   if (((m->clas) && (!ec->icccm.class)) ||
       ((ec->icccm.class) && (m->clas) && (!e_util_glob_match(ec->icccm.class, m->clas))))
     return EINA_FALSE;
   if (((m->role) && (!ec->icccm.window_role)) ||
       ((ec->icccm.window_role) && (m->role) && (!e_util_glob_match(ec->icccm.window_role, m->role))))
     return EINA_FALSE;
   if (m->primary_type)
     {
        if (ec->netwm.type)
          {
             if ((int)ec->netwm.type != m->primary_type)
               return EINA_FALSE;
          }
        else if (m->primary_type != E_WINDOW_TYPE_REAL_UNKNOWN)
          return EINA_FALSE;
     }
  if (m->borderless != 0)
    {
       int borderless = 0;

       if (e_client_util_borderless(ec))
         borderless = 1;
       if (!(((m->borderless == -1) && (!borderless)) ||
             ((m->borderless == 1) && (borderless))))
         return EINA_FALSE;
    }
  if (m->dialog != 0)
    {
       int dialog = 0;

       if (((ec->icccm.transient_for != 0) ||
            (ec->dialog)))
         dialog = 1;
       if (!(((m->dialog == -1) && (!dialog)) ||
             ((m->dialog == 1) && (dialog))))
         return EINA_FALSE;
    }
  if (m->accepts_focus != 0)
    {
       int accepts_focus = 0;

       if (ec->icccm.accepts_focus)
         accepts_focus = 1;
       if (!(((m->accepts_focus == -1) && (!accepts_focus)) ||
             ((m->accepts_focus == 1) && (accepts_focus))))
         return EINA_FALSE;
    }
  if (m->vkbd != 0)
    {
       int vkbd = 0;

       if (ec->vkbd.vkbd)
         vkbd = 1;
       if (!(((m->vkbd == -1) && (!vkbd)) ||
             ((m->vkbd == 1) && (vkbd))))
         return EINA_FALSE;
    }
  if (m->argb != 0)
    {
       if (!(((m->argb == -1) && (!ec->argb)) ||
             ((m->argb == 1) && (ec->argb))))
         return EINA_FALSE;
    }
  if (m->fullscreen != 0)
    {
       int fullscreen = ec->fullscreen;

       if (!(((m->fullscreen == -1) && (!fullscreen)) ||
             ((m->fullscreen == 1) && (fullscreen))))
         return EINA_FALSE;
    }
  if (m->modal != 0)
    {
       int modal = 0;

       if (ec->netwm.state.modal)
         modal = 1;
       if (!(((m->modal == -1) && (!modal)) ||
             ((m->modal == 1) && (modal))))
         return EINA_FALSE;
    }
  return EINA_TRUE;
}

/* function for setting up a client's compositor frame theme (cw->shobj) */
static Eina_Bool
_e_comp_object_shadow_setup(E_Comp_Object *cw)
{
   int ok = 0;
   char buf[4096];
   Eina_List *list = NULL, *l;
   E_Comp_Match *m;
   Eina_Stringshare *reshadow_group = NULL;
   Eina_Bool focus = EINA_FALSE, urgent = EINA_FALSE, skip = EINA_FALSE,
             reshadow = EINA_FALSE, no_shadow = EINA_FALSE, override;
   Eina_Stringshare *name, *title;
   E_Comp_Config *conf = e_comp_config_get();

   edje_object_file_get(cw->shobj, NULL, &reshadow_group);
   override = cw->ec->override || (cw->ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU);
   /* match correct client type */
   list = override ? conf->match.overrides : conf->match.borders;
   name = cw->ec->icccm.name;
   title = cw->ec->icccm.title;
   skip = (title && (!strncmp(title, "noshadow", 8)));

   /* skipping here is mostly a hack for systray because I hate it */
   if (!skip)
     {
        EINA_LIST_FOREACH(list, l, m)
          {
             if (((m->name) && (!name)) ||
                 ((name) && (m->name) && (!e_util_glob_match(name, m->name))))
               continue;
             if (!_e_comp_object_shadow_client_match(cw->ec, m)) continue;

             focus = m->focus;
             urgent = m->urgent;
             no_shadow = m->no_shadow;
             if (m->shadow_style)
               {
                  if (!ok)
                    {
                       snprintf(buf, sizeof(buf), "e/comp/frame/%s", m->shadow_style);
                       reshadow = ok = !e_util_strcmp(reshadow_group, buf);
                       if (!ok)
                         ok = e_theme_edje_object_set(cw->shobj, "base/theme/comp", buf);
                    }
                  if (ok && m->visibility_effect)
                    eina_stringshare_refplace(&cw->visibility_effect, m->visibility_effect);
                  if (ok) break;
               }
          }
     }
   while (!ok)
     {
        if (skip || (cw->ec->e.state.video))
          {
             reshadow = ok = !e_util_strcmp(reshadow_group, "e/comp/frame/none");
             if (!ok)
               ok = e_theme_edje_object_set(cw->shobj, "base/theme/comp", "e/comp/frame/none");
          }
        if (ok) break;
        if (conf->shadow_style)
          {
             if (!ok)
               {
                  snprintf(buf, sizeof(buf), "e/comp/frame/%s", conf->shadow_style);
                  reshadow = ok = !e_util_strcmp(reshadow_group, buf);
                  if (!ok)
                    ok = e_theme_edje_object_set(cw->shobj, "base/theme/comp", buf);
               }
          }
        if (!ok)
          {
             reshadow = ok = !e_util_strcmp(reshadow_group, "e/comp/frame/default");
             if (!ok)
               ok = e_theme_edje_object_set(cw->shobj, "base/theme/comp", "e/comp/frame/default");
          }
        break;
     }
   /* reshadow means this entire function call has been a no-op since we're re-setting the current style */
   if (reshadow)
     {
        if (cw->zoomap_disabled)
          {
             if (cw->frame_object && (cw->zoomobj && e_zoomap_child_get(cw->zoomobj) == cw->frame_object)) return EINA_FALSE;
          }
        else
          {
             if (cw->frame_object && (edje_object_part_swallow_get(cw->shobj, "e.swallow.content") == cw->frame_object)) return EINA_FALSE;
          }
     }
   if (override)
     {
        if ((!cw->ec->shaped) && (!no_shadow) && (!cw->ec->argb))
          edje_object_signal_emit(cw->shobj, "e,state,shadow,on", "e");
        else
          edje_object_signal_emit(cw->shobj, "e,state,shadow,off", "e");
        evas_object_smart_callback_call(cw->smart_obj, "shadow_change", cw->ec);
     }
   else
     {
        if (no_shadow)
          {
             edje_object_signal_emit(cw->shobj, "e,state,shadow,off", "e");
             evas_object_smart_callback_call(cw->smart_obj, "shadow_change", cw->ec);
          }
        else
          _e_comp_object_shadow(cw);
     }

   if (focus || cw->ec->focused || override)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,focused", "e");
   else
     e_comp_object_signal_emit(cw->smart_obj, "e,state,unfocused", "e");
   if (urgent || cw->ec->urgent)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,urgent", "e");
   else
     e_comp_object_signal_emit(cw->smart_obj, "e,state,not_urgent", "e");
   if (cw->ec->shaded)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,shaded", "e");
   if (cw->ec->sticky)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,sticky", "e");
   if (cw->ec->hung)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,hung", "e");
   /* visibility must always be enabled for re_manage clients to prevent
    * pop-in animations every time the user sees a persistent client again;
    * applying visibility for iconic clients prevents the client from getting
    * stuck as hidden
    */
   if (cw->visible || cw->ec->iconic || cw->ec->re_manage)
     {
        if ((cw->w > 0) && (cw->h > 0))
          e_comp_object_signal_emit(cw->smart_obj, "e,state,visible", "e");
     }
   else
     e_comp_object_signal_emit(cw->smart_obj, "e,state,hidden", "e");
   if (e_comp_object_frame_allowed(cw->smart_obj))
     e_comp_object_signal_emit(cw->smart_obj, "e,state,focus,enabled", "e");
   else
     e_comp_object_signal_emit(cw->smart_obj, "e,state,focus,disabled", "e");

   /* breaks animation counter */
   //if (cw->ec->iconic)
     //e_comp_object_signal_emit(cw->smart_obj, "e,action,iconify", "e");
   if (!cw->zoomap_disabled)
     e_zoomap_child_set(cw->zoomobj, NULL);
   if (cw->frame_object)
     {
        edje_object_part_swallow(cw->frame_object, "e.swallow.client", cw->obj);
        edje_object_part_swallow(cw->frame_object, "e.swallow.icon", cw->frame_icon);
        edje_object_part_swallow(cw->frame_object, "e.swallow.volume", cw->frame_volume);
        if (cw->zoomap_disabled)
          edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->frame_object);
        else
          {
             e_zoomap_child_set(cw->zoomobj, cw->frame_object);
             edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->zoomobj);
          }
        no_shadow = 1;
     }
   else
     {
        no_shadow = 1;
        if (cw->zoomobj)
          {
             e_zoomap_child_set(cw->zoomobj, cw->obj);
             edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->zoomobj);
          }
        else
          edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->obj);
     }
   if (cw->input_objs)
     evas_object_pass_events_set(cw->obj, 1);
   else
     evas_object_pass_events_set(cw->obj, 0);
#ifdef BORDER_ZOOMAPS
   e_zoomap_child_edje_solid_setup(cw->zoomobj);
#endif
   return EINA_TRUE;
}

/////////////////////////////////////////////

static void
_e_comp_object_animating_begin(E_Comp_Object *cw)
{
   cw->animating++;
   if (cw->animating == 1)
     {
        e_comp->animating++;
        REFD(cw->ec, 2);
        e_object_ref(E_OBJECT(cw->ec));
        evas_object_image_border_center_fill_set(cw->obj, EVAS_BORDER_FILL_DEFAULT);
     }
}

static Eina_Bool
_e_comp_object_animating_end(E_Comp_Object *cw)
{
   if (cw->animating)
     {
        cw->animating--;
        if (!cw->animating)
          {
             e_comp->animating--;
             cw->showing = 0;
             evas_object_image_border_center_fill_set(cw->obj, EVAS_BORDER_FILL_SOLID);
             UNREFD(cw->ec, 2);
             /* remove ref from animation start, account for possibility of deletion from unref */
             return !!e_object_unref(E_OBJECT(cw->ec));
          }
     }
   return EINA_TRUE;
}

/* handle the end of a compositor animation */
static void
_e_comp_object_done_defer(void *data, Evas_Object *obj EINA_UNUSED, const char *emission, const char *source EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   //INF("DONE DEFER %p: %dx%d - %s", cw->ec, cw->w, cw->h, emission);
   /* visible clients which have never been sized are a bug */
   if ((!cw->ec->new_client) && (!cw->ec->changes.size) && ((cw->w < 0) || (cw->h < 0)) && (!strcmp(emission, "e,action,show,done")))
     CRI("ACK!");
   if (!_e_comp_object_animating_end(cw)) return;
   if (cw->animating) return;
   /* hide only after animation finishes to guarantee a full run of the animation */
   if (cw->defer_hide && ((!strcmp(emission, "e,action,hide,done")) || (!strcmp(emission, "e,action,done"))))
     evas_object_hide(cw->smart_obj);
   else if (!cw->deleted)
     {
        e_comp_shape_queue();
        if (cw->visible && cw->updates_exist)
          e_comp_object_render_update_add(cw->smart_obj);
     }
}

/* run a visibility compositor effect if available, return false if object is dead */
static Eina_Bool
_e_comp_object_effect_visibility_start(E_Comp_Object *cw, Eina_Bool state)
{
   int x, y, zw, zh;

   if ((!cw->visibility_effect) || (!e_comp_object_effect_allowed_get(cw->smart_obj))) return EINA_TRUE;;
   if (!cw->effect_running)
     _e_comp_object_animating_begin(cw);
   if (!e_comp_object_effect_stop(cw->smart_obj, _e_comp_object_done_defer))
     return _e_comp_object_animating_end(cw);
   if (!e_comp_object_effect_set(cw->smart_obj, cw->visibility_effect))
     return _e_comp_object_animating_end(cw);
   /* mouse position is not available for some windows under X11
    * only fetch pointer position if absolutely necessary
    */
   if (edje_object_data_get(cw->effect_obj, "need_pointer") &&
       (e_comp->comp_type == E_PIXMAP_TYPE_X))
     ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   else
     evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
   x -= cw->ec->x;
   y -= cw->ec->y;
   if (cw->ec->zone)
     zw = cw->ec->zone->w, zh = cw->ec->zone->h;
   else
     {
        E_Zone *zone;

        zone = e_comp_object_util_zone_get(cw->smart_obj);
        if (!zone) zone = e_zone_current_get();
        zw = zone->w, zh = zone->h;
     }
   e_comp_object_effect_params_set(cw->smart_obj, 1, (int[]){cw->ec->x, cw->ec->y,
      cw->ec->w, cw->ec->h, zw, zh, x, y}, 8);
   e_comp_object_effect_params_set(cw->smart_obj, 0, (int[]){state}, 1);
   e_comp_object_effect_start(cw->smart_obj, _e_comp_object_done_defer, cw);
   return EINA_TRUE;
}
/////////////////////////////////////////////

/* create necessary objects for clients that e manages */
static void
_e_comp_object_setup(E_Comp_Object *cw)
{
   cw->clip = evas_object_rectangle_add(e_comp->evas);
   evas_object_resize(cw->clip, 999999, 999999);
   evas_object_smart_member_add(cw->clip, cw->smart_obj);
   cw->effect_obj = edje_object_add(e_comp->evas);
   evas_object_move(cw->effect_obj, cw->x, cw->y);
   evas_object_clip_set(cw->effect_obj, cw->clip);
   evas_object_smart_member_add(cw->effect_obj, cw->smart_obj);
   e_theme_edje_object_set(cw->effect_obj, "base/theme/comp", "e/comp/effects/none");
   cw->shobj = edje_object_add(e_comp->evas);
   evas_object_data_set(cw->shobj, "comp_smart_obj", cw->smart_obj);
   edje_object_part_swallow(cw->effect_obj, "e.swallow.content", cw->shobj);
   edje_object_signal_callback_add(cw->shobj, "e,action,*,done", "e", _e_comp_object_done_defer, cw);

   /* name objects appropriately for nicer printing when using e_comp_util_wins_print() */
   if (cw->ec->override)
     {
        evas_object_name_set(cw->shobj, "cw->shobj::WINDOW");
        evas_object_name_set(cw->effect_obj, "cw->effect_obj::WINDOW");
        evas_object_name_set(cw->clip, "cw->clip::WINDOW");
     }
   else if (!cw->ec->input_only)
     {
        evas_object_name_set(cw->shobj, "cw->shobj::CLIENT");
        evas_object_name_set(cw->effect_obj, "cw->effect_obj::CLIENT");
        evas_object_name_set(cw->clip, "cw->clip::CLIENT");
     }
   cw->real_hid = !cw->ec->input_only;
   if (!cw->ec->input_only)
     {
        //e_util_size_debug_set(cw->clip, 1);
        e_util_size_debug_set(cw->effect_obj, 1);
        //e_util_size_debug_set(cw->shobj, 1);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_IN, _e_comp_object_cb_mouse_in, cw);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_OUT, _e_comp_object_cb_mouse_out, cw);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_DOWN, _e_comp_object_cb_mouse_down, cw);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_UP, _e_comp_object_cb_mouse_up, cw);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_MOVE, _e_comp_object_cb_mouse_move, cw);
        evas_object_event_callback_add(cw->smart_obj, EVAS_CALLBACK_MOUSE_WHEEL, _e_comp_object_cb_mouse_wheel, cw);
     }
}

static void
_e_comp_object_mirror_pixels_get(void *data, Evas_Object *obj)
{
   E_Comp_Object *cw = data;
   E_Client *ec = cw->ec;
   int pw, ph;

   if ((!ec->pixmap) || (!e_pixmap_size_get(ec->pixmap, &pw, &ph)))
     {
        evas_object_image_data_set(obj, NULL);
        return;
     }

   /* This is a big fat hack - ideally we're already on this list
    * if the parent is visible, but there are some circumstances
    * where a client receives damage while visible but its own pixels_get
    * callback doesn't fire (new damage during the start frame of a desk
    * switch animation).
    * Thus we can't make this addition conditional on visibility or we can
    * (under wayland at least) lose a frame callback and stop updating.
    *
    * e_comp_client_post_update_add() prevents clients from being
    * on the list twice, so this is theoretically not harmful.
    */
   e_comp_client_post_update_add(ec);

   if (cw->native) return;

   evas_object_image_data_set(obj, e_pixmap_image_data_get(cw->ec->pixmap));
}

/////////////////////////////////////////////

/* for fast path evas rendering; only called during render */
static void
_e_comp_object_pixels_get(void *data, Evas_Object *obj)
{
   E_Comp_Object *cw = data;
   E_Client *ec = cw->ec;
   int pw, ph;

   if ((!ec->pixmap) || (!e_pixmap_size_get(ec->pixmap, &pw, &ph)))
     {
        evas_object_image_data_set(obj, NULL);
        return;
     }
   //INF("PIXEL GET %p: %dx%d || %dx%d", ec, ec->w, ec->h, pw, ph);

   /* queue another render if client is still dirty; cannot refresh here. */
   if (e_pixmap_dirty_get(ec->pixmap) && e_pixmap_size_get(ec->pixmap, &pw, &ph))
     {
        e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
        /* if updates for existing pixmap don't exist then avoid unsetting existing image */
        if ((!cw->pending_updates) || eina_tiler_empty(cw->pending_updates)) return;
     }

   if (cw->native)
     {
        E_FREE_FUNC(cw->pending_updates, eina_tiler_free);
        e_comp_client_post_update_add(cw->ec);
     }
   else if (e_comp_object_render(ec->frame))
     {
        /* apply shape mask if necessary */
        if ((!cw->native) && (ec->shaped || ec->shape_changed))
          e_comp_object_shape_apply(ec->frame);
        ec->shape_changed = 0;
     }
   if (e_object_is_del(E_OBJECT(ec))) return;
   /* shaped clients get precise mouse events to handle transparent pixels */
   evas_object_precise_is_inside_set(cw->obj, ec->shaped || ec->shaped_input);

   //INF("%p PX(%dx%d) EC(%dx%d) CW(%dx%d)", ec, pw, ph, ec->w, ec->h, cw->w, cw->h);
   //e_comp_object_frame_wh_adjust(cw->smart_obj, pw, ph, &pw, &ph);
   //if ((ec->w != pw) || (ec->h != ph))
     //{
        ///* DO NOT RESIZE HERE. */
        //INF("CW RSZ FIX: %dx%d -> %dx%d", ec->w, ec->h, pw, ph);
        //ec->w = pw, ec->h = ph;
        //ec->changes.size = 1;
        //EC_CHANGED(ec);
     //}
}

/////////////////////////////////////////////

static void
_e_comp_object_ssd_mouse_in(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   E_Comp_Object *cw = data;

   evas_object_smart_callback_call(cw->smart_obj, "mouse_in", event_info);
}

static void
_e_comp_object_ssd_mouse_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   E_Comp_Object *cw = data;

   evas_object_smart_callback_call(cw->smart_obj, "mouse_out", event_info);
}

/////////////////////////////////////////////

static void
_e_comp_object_client_pending_resize_add(E_Client *ec,
                                         int w,
                                         int h,
                                         unsigned int serial)
{
   E_Client_Pending_Resize *pnd;

   pnd = E_NEW(E_Client_Pending_Resize, 1);
   if (!pnd) return;
   pnd->w = w;
   pnd->h = h;
   pnd->serial = serial;
   ec->pending_resize = eina_list_append(ec->pending_resize, pnd);
}

static void
_e_comp_intercept_move(void *data, Evas_Object *obj, int x, int y)
{
   E_Comp_Object *cw = data;
   int ix, iy, fx, fy;

   /* if frame_object does not exist, client_inset indicates CSD.
    * this means that ec->client matches cw->x/y, the opposite
    * of SSD.
    */
   fx = (!cw->frame_object) * cw->client_inset.l;
   fy = (!cw->frame_object) * cw->client_inset.t;
   if ((cw->x == x + fx) && (cw->y == y + fy))
     {
        if ((cw->ec->x != x) || (cw->ec->y != y))
          {
             /* handle case where client tries to move to position and back very quickly */
             cw->ec->x = x, cw->ec->y = y;
             cw->ec->client.x = x + cw->client_inset.l;
             cw->ec->client.y = y + cw->client_inset.t;
          }
        return;
     }
   if (!cw->ec->maximize_override)
     {
        /* prevent moving in some directions while directionally maximized */
        if ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_VERTICAL)
          y = cw->y;
        if ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_HORIZONTAL)
          x = cw->x;
     }
   ix = x + cw->client_inset.l;
   iy = y + cw->client_inset.t;
   if (cw->ec->maximized && (!cw->ec->maximize_override) && ((cw->ec->x != x) || (cw->ec->y != y)) &&
       ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) != E_MAXIMIZE_VERTICAL) &&
       ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) != E_MAXIMIZE_HORIZONTAL))
     {
        /* prevent moving at all if move isn't allowed in current maximize state */
        if ((!e_config->allow_manip) && ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_BOTH)) return;
        /* queue unmaximize if we are allowing move and update unmaximize geometry */
        if ((!cw->ec->shading) && (!cw->ec->shaded))
          {
             cw->ec->changes.need_unmaximize = 1;
             cw->ec->saved.x = ix - cw->ec->zone->x;
             cw->ec->saved.y = iy - cw->ec->zone->y;
             cw->ec->saved.w = cw->ec->client.w;
             cw->ec->saved.h = cw->ec->client.h;
             EC_CHANGED(cw->ec);
             return;
          }
        return;
     }
   /* only update during resize if triggered by resize */
   if (e_client_util_resizing_get(cw->ec) && (!cw->force_move)) return;
   cw->ec->x = x, cw->ec->y = y;
   if (cw->ec->new_client)
     {
        /* don't actually do anything until first client idler loop */
        if (!cw->ec->placed)
          cw->ec->placed = ((!cw->ec->dialog) && (!cw->ec->parent));
        cw->ec->changes.pos = 1;
        EC_CHANGED(cw->ec);
     }
   else
     {
        /* only update xy position of client to avoid invalid
         * first damage region if it is not a new_client. */
        cw->ec->placed = 1;
        if (!cw->ec->shading)
          {
             cw->ec->client.x = ix;
             cw->ec->client.y = iy;
          }
        /* flip SSD->CSD */
        if (!cw->frame_object)
          x = ix, y = iy;
        evas_object_move(obj, x, y);
     }
}

static void
_e_comp_intercept_resize(void *data, Evas_Object *obj, int w, int h)
{
   E_Comp_Object *cw = data;
   int pw = 0, ph = 0, fw, fh, iw, ih, prev_w, prev_h, x, y;


   if ((w < 1) || (h < 1)) return;
   /* if frame_object does not exist, client_inset indicates CSD.
    * this means that ec->client matches cw->w/h, the opposite
    * of SSD.
    */
   fw = (!cw->frame_object) * (-cw->client_inset.l - cw->client_inset.r);
   fh = (!cw->frame_object) * (-cw->client_inset.t - cw->client_inset.b);
   if ((cw->w == w + fw) && (cw->h == h + fh))
     {
        if (cw->ec->shading || cw->ec->shaded) return;
        if (((cw->ec->w != w) || (cw->ec->h != h)) ||
            (cw->ec->client.w != w - cw->client_inset.l - cw->client_inset.r) ||
            (cw->ec->client.h != h - cw->client_inset.t - cw->client_inset.b))
          {
             /* handle case where client tries to resize itself and back very quickly */
             cw->ec->w = w, cw->ec->h = h;
             cw->ec->client.w = w - cw->client_inset.l - cw->client_inset.r;
             cw->ec->client.h = h - cw->client_inset.t - cw->client_inset.b;
             evas_object_smart_callback_call(obj, "client_resize", NULL);
          }
        return;
     }
   /* guarantee that fullscreen is fullscreen */
   if (cw->ec->fullscreen && ((w != cw->ec->zone->w) || (h != cw->ec->zone->h)))
     return;
   /* calculate client size */
   iw = w - cw->client_inset.l - cw->client_inset.r;
   ih = h - cw->client_inset.t - cw->client_inset.b;
   if (cw->ec->maximized && (!cw->ec->maximize_override) && ((cw->ec->w != w) || (cw->ec->h != h)))
     {
        /* prevent resizing while maximized depending on direction and config */
        if ((!e_config->allow_manip) && ((cw->ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_BOTH)) return;
        if ((!cw->ec->shading) && (!cw->ec->shaded))
          {
             Eina_Bool reject = EINA_FALSE;
             if (cw->ec->maximized & E_MAXIMIZE_VERTICAL)
               {
                  if (cw->ec->client.h != ih)
                    {
                       cw->ec->saved.h = ih;
                       cw->ec->saved.y = cw->ec->client.y - cw->ec->zone->y;
                       reject = cw->ec->changes.need_unmaximize = 1;
                    }
               }
             if (cw->ec->maximized & E_MAXIMIZE_HORIZONTAL)
               {
                  if (cw->ec->client.w != iw)
                    {
                       cw->ec->saved.w = iw;
                       cw->ec->saved.x = cw->ec->client.x - cw->ec->zone->x;
                       reject = cw->ec->changes.need_unmaximize = 1;
                    }
               }
             if (reject)
               {
                  EC_CHANGED(cw->ec);
                  return;
               }
          }
     }
   if (cw->ec->new_client || (!cw->ec->visible) || (!cw->effect_obj))
     {
        /* do nothing until client idler loops */
        if (!cw->ec->maximized)
          {
             /* NO. DO NOT DO THIS OR YOU WILL BREAK WINDOW RESIZING. */
             //if ((w != cw->ec->w) || (h != cw->ec->h))
               {
                  cw->ec->w = w, cw->ec->h = h;
                  cw->ec->changes.size = 1;
                  EC_CHANGED(cw->ec);
               }
          }
        return;
     }
   if ((!cw->ec->internal) && e_client_util_resizing_get(cw->ec) && cw->ec->netwm.sync.request &&
       ((cw->ec->w != w) || (cw->ec->h != h)))
     {
        /* this is ugly. */
        //INF("PENDING %dx%d", iw, ih);
        /* netwm sync resizes queue themselves and then trigger later on */
        _e_comp_object_client_pending_resize_add(cw->ec, iw, ih, cw->ec->netwm.sync.serial);
     }
   cw->ec->w = w, cw->ec->h = h;
   if ((!cw->ec->shading) && (!cw->ec->shaded))
     {
        /* client geom never changes when shading since the client is never altered */
        INF("%p: CUR(%dx%d) || REQ(%dx%d)", cw->ec, cw->ec->client.w, cw->ec->client.h, iw, ih);
        cw->ec->client.w = iw;
        cw->ec->client.h = ih;
        if ((cw->ec->client.w < 0) || (cw->ec->client.h < 0)) CRI("WTF");
     }
   if ((!cw->ec->input_only) && cw->redirected && (!cw->ec->shading) && (!cw->ec->shaded) &&
       (e_pixmap_dirty_get(cw->ec->pixmap) || (!e_pixmap_size_get(cw->ec->pixmap, &pw, &ph))))
     {
        if (e_comp->comp_type != E_PIXMAP_TYPE_X) return;
        if (e_object_is_del(E_OBJECT(cw->ec))) return;
        /* shapeless clients smh */
        if (cw->ec->shaped && (!cw->ec->shape_rects)) return;
        /* pending shape change gtfo */
        if (!cw->ec->changes.shape)
          {
             /* client can't be resized if its pixmap isn't usable, try again */
             e_pixmap_dirty(cw->ec->pixmap);
             if (e_comp->nocomp)
               e_pixmap_refresh(cw->ec->pixmap);
             else
               {
                  e_comp_object_render_update_add(obj);
                  e_comp_render_queue();
               }
          }
        cw->ec->changes.size = 1;
        EC_CHANGED(cw->ec);
        return;
     }
   if (e_pixmap_failures_get(cw->ec->pixmap) && (!cw->redirected))
     {
        e_comp_object_redirected_set(obj, 1);
        return;
     }
   prev_w = cw->w, prev_h = cw->h;
   e_comp_object_frame_wh_adjust(obj, 0, 0, &fw, &fh);
   /* check shading and clamp to pixmap size for regular clients */
   if ((!cw->ec->shading) && (!cw->ec->shaded) && (!cw->ec->input_only) && (!cw->ec->override) &&
       (((w - fw != pw) || (h - fh != ph))))
     {
        //INF("CALLBACK: REQ(%dx%d) != CUR(%dx%d)", w - fw, h - fh, pw, ph);
        evas_object_smart_callback_call(obj, "client_resize", NULL);
        /* flip for CSD */
        if (cw->frame_object || cw->ec->input_only)
          e_comp_object_frame_wh_adjust(obj, pw, ph, &w, &h);
        else
          w = pw, h = ph;
        if ((cw->w == w) && (cw->h == h))
          {
             /* going to be a noop resize which won't trigger smart resize */
             RENDER_DEBUG("DAMAGE RESIZE(%p): %dx%d", cw->ec, cw->ec->client.w, cw->ec->client.h);
             if (cw->updates) eina_tiler_area_size_set(cw->updates, cw->ec->client.w, cw->ec->client.h);
          }
        evas_object_resize(obj, w, h);
     }
   else
     {
        /* flip for CSD */
        if ((!cw->frame_object) && (!cw->ec->input_only))
          w = pw, h = ph;
        /* "just do it" for overrides */
        //INF("INTERCEPT %dx%d", w, h);
        evas_object_resize(obj, w, h);
     }
   if (!cw->ec->override)
     {
        /* shape probably changed for non-overrides */
        cw->ec->need_shape_merge |= cw->ec->shaped || cw->ec->shaped_input;
        cw->ec->need_shape_export |= cw->ec->shaped;
        if (cw->ec->shaped || cw->ec->shaped_input)
          EC_CHANGED(cw->ec);
     }

   /* this fixes positioning jiggles when using a resize mode
    * which also changes the client's position
    */
   cw->force_move = 1;
   if (cw->frame_object)
     x = cw->x, y = cw->y;
   else
     x = cw->ec->x, y = cw->ec->y;
   switch (cw->ec->resize_mode)
     {
      case E_POINTER_RESIZE_BL:
      case E_POINTER_RESIZE_L:
        evas_object_move(obj, x + prev_w - cw->w, y);
        break;
      case E_POINTER_RESIZE_TL:
        evas_object_move(obj, x + prev_w - cw->w, y + prev_h - cw->h);
        break;
      case E_POINTER_RESIZE_T:
      case E_POINTER_RESIZE_TR:
        evas_object_move(obj, x, y + prev_h - cw->h);
        break;
      default:
        break;
     }
   if (cw->ec->internal_elm_win && (!cw->ec->moving) && (!e_client_util_resizing_get(cw->ec)) &&
       (!cw->ec->fullscreen) && (!cw->ec->maximized) &&
       e_win_centered_get(cw->ec->internal_elm_win))
     {
        e_comp_object_util_center(obj);
     }
   cw->force_move = 0;
}

static void
_e_comp_intercept_layer_set(void *data, Evas_Object *obj, int layer)
{
   E_Comp_Object *cw = data;
   unsigned int l = e_comp_canvas_layer_map(layer);
   int oldraise;
   E_Client *ec;

   if (cw->ec->layer_block)
     {
        /* doing a compositor effect, follow directions */
        evas_object_layer_set(obj, layer);
        if (layer == cw->ec->layer) //trying to put layer back
          {
             if (cw->visible)
               {
                  e_comp_shape_queue();
                  e_comp_render_queue();
               }
             ec = e_client_above_get(cw->ec);
             if (ec && (evas_object_layer_get(ec->frame) != evas_object_layer_get(obj)))
               {
                  ec = e_client_below_get(cw->ec);
                  if (ec && (evas_object_layer_get(ec->frame) == evas_object_layer_get(cw->smart_obj)))
                    {
                       evas_object_stack_above(obj, ec->frame);
                       return;
                    }
                  ec = NULL;
               }
             if (ec && (cw->ec->parent == ec))
               evas_object_stack_above(obj, ec->frame);
             else
               evas_object_stack_below(obj, ec ? ec->frame : e_comp->layers[cw->layer].obj);
          }
        return;
     }
   if (cw->layer == l) return;
   if (e_comp_canvas_client_layer_map(layer) == 9999)
     return; //invalid layer for clients not doing comp effects
   if (cw->ec->fullscreen)
     {
        cw->ec->saved.layer = layer;
        return;
     }
   oldraise = e_config->transient.raise;
   _e_comp_object_layers_remove(cw);
   /* clamp to valid client layer */
   layer = e_comp_canvas_client_layer_map_nearest(layer);
   ec = cw->ec;
   ec->layer = layer;
   if (ec->stack.prev || ec->stack.next)
     {
        if (ec->stack.ignore == 0)
          {
             Eina_List *ll, *list = e_client_stack_list_prepare(ec);
             E_Client *child;

             EINA_LIST_FOREACH(list, ll, child)
               {
                  if (child == ec) continue;
                  evas_object_layer_set(child->frame, layer);
               }
             e_client_stack_list_finish(list);
             evas_object_raise(ec->frame);
          }
     }
   else
     {
        if (e_config->transient.layer)
          {
             E_Client *child;
             Eina_List *list = eina_list_clone(cw->ec->transients);

             /* We need to set raise to one, else the child won't
              * follow to the new layer. It should be like this,
              * even if the user usually doesn't want to raise
              * the transients.
              */
             e_config->transient.raise = 1;
             EINA_LIST_FREE(list, child)
               evas_object_layer_set(child->frame, layer);
          }
     }
   if (!cw->ec->override)
     {
        /* set client stacking hints based on layer */
        if (layer == E_LAYER_CLIENT_BELOW)
          e_hints_window_stacking_set(cw->ec, E_STACKING_BELOW);
        else if (layer == E_LAYER_CLIENT_ABOVE)
          e_hints_window_stacking_set(cw->ec, E_STACKING_ABOVE);
        else
          e_hints_window_stacking_set(cw->ec, E_STACKING_NONE);
     }

   e_config->transient.raise = oldraise;
   cw->layer = e_comp_canvas_layer_map(layer);
   _e_comp_object_layers_add(cw, NULL, NULL, 0);
   //if (cw->ec->new_client)
     //INF("CLIENT STACKED %p: %u", cw->ec, layer);
   evas_object_layer_set(obj, layer);
   if (!e_comp->layers[cw->layer].obj) return; //this is a layer marker
   evas_object_stack_below(obj, e_comp->layers[cw->layer].obj);
   if (evas_object_below_get(obj) == e_comp->layers[cw->layer].obj)
     {
        /* can't stack a client above its own layer marker */
        CRI("STACKING ERROR!!!");
     }
   if (!cw->visible) return;
   e_comp_render_queue();
   e_comp_shape_queue();
}

typedef void (*E_Comp_Object_Stack_Func)(Evas_Object *obj, Evas_Object *stack);

static void
_e_comp_intercept_stack_helper(E_Comp_Object *cw, Evas_Object *stack, E_Comp_Object_Stack_Func stack_cb)
{
   E_Comp_Object *cw2 = NULL;
   E_Client *ecstack;
   short layer;
   Evas_Object *o = stack;
   Eina_Bool raising = stack_cb == evas_object_stack_above;

   if (cw->ec->layer_block)
     {
        /* obey compositor effects! */
        if (cw->ec->layer == evas_object_layer_get(cw->smart_obj))
          evas_object_data_set(cw->smart_obj, "client_restack", (void*)1);
        stack_cb(cw->smart_obj, stack);
        if (cw->ec->layer == evas_object_layer_get(cw->smart_obj))
          evas_object_data_del(cw->smart_obj, "client_restack");
        return;
     }
   /* assume someone knew what they were doing during client init */
   if (cw->ec->new_client)
     layer = cw->ec->layer;
   else
     layer = evas_object_layer_get(stack);
   ecstack = e_client_below_get(cw->ec);
   if (layer != e_comp_canvas_layer_map_to(cw->layer))
     {
        /* some FOOL is trying to restack a layer marker */
        if (cw->smart_obj == e_comp->layers[cw->layer].obj) return;
        evas_object_layer_set(cw->smart_obj, layer);
        /* we got our layer wrangled, return now! */
        if (layer != e_comp_canvas_layer_map_to(cw->layer)) return;
     }

   /* check if we're stacking below another client */
   cw2 = evas_object_data_get(o, "comp_obj");
   while (!cw2)
     {
        /* check for non-client layer object */
        if (!e_util_strcmp(evas_object_name_get(o), "layer_obj"))
          break;
        /* find an existing client to use for layering
         * by walking up the object stack
         *
         * this is guaranteed to be pretty quick since we'll either:
         * - run out of client layers
         * - find a stacking client
         */
        o = evas_object_above_get(o);
        if ((!o) || (o == cw->smart_obj)) break;
        if (evas_object_layer_get(o) != layer)
          {
             /* reached the top client layer somehow
              * use top client object
              */
             o = e_comp->layers[e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO)].obj;
          }
        if (!o)
          /* top client layer window hasn't been stacked yet. this probably shouldn't happen?
           * return here since the top client layer window
           */
          {
             E_Client *ec;

             ec = e_client_top_get();
             if (ec)
               o = ec->frame;
             //else //wat
          }
        if (o) cw2 = evas_object_data_get(o, "comp_obj");
     }


   /* remove existing layers */
   _e_comp_object_layers_remove(cw);
   if (cw2)
     {
        if (o == stack) //if stacking above, cw2 is above; else cw2 is below
          _e_comp_object_layers_add(cw, raising ? cw2 : NULL, raising ? NULL : cw2, 0);
        else if (o == cw->smart_obj) //prepend (lower) if not stacking above
          _e_comp_object_layers_add(cw, NULL, NULL, !raising);
        else //if no stacking objects found, either raise or lower
          _e_comp_object_layers_add(cw, raising ? NULL : cw2, raising ? cw2 : NULL, 0);
     }
   else
     _e_comp_object_layers_add(cw, NULL, NULL, 0);
   /* set restack if stacking has changed */
   if (cw->ec->new_client || (!ecstack) || (ecstack->frame != o))
     evas_object_data_set(cw->smart_obj, "client_restack", (void*)1);
   stack_cb(cw->smart_obj, stack);
   if (e_comp->layers[cw->layer].obj)
     if (evas_object_below_get(cw->smart_obj) == e_comp->layers[cw->layer].obj)
       {
          CRI("STACKING ERROR!!!");
       }
   if (cw->ec->new_client || (!ecstack) || (ecstack->frame != o))
     evas_object_data_del(cw->smart_obj, "client_restack");
   if (!cw->visible) return;
   e_comp_render_queue();
   e_comp_shape_queue();
}

static void
_e_comp_intercept_stack_above(void *data, Evas_Object *obj, Evas_Object *above)
{
   EINA_SAFETY_ON_TRUE_RETURN(obj == above);
   if (evas_object_below_get(obj) == above) return;
   _e_comp_intercept_stack_helper(data, above, evas_object_stack_above);
}

static void
_e_comp_intercept_stack_below(void *data, Evas_Object *obj, Evas_Object *below)
{
   EINA_SAFETY_ON_TRUE_RETURN(obj == below);
   if (evas_object_above_get(obj) == below) return;
   _e_comp_intercept_stack_helper(data, below, evas_object_stack_below);
}

static void
_e_comp_intercept_lower(void *data, Evas_Object *obj)
{
   E_Comp_Object *cw = data;
   Evas_Object *o;

   if (cw->ec->layer_block)
     {
        evas_object_lower(obj);
        return;
     }
   if (!EINA_INLIST_GET(cw->ec)->prev) return; //already lowest on layer
   o = evas_object_below_get(obj);
   _e_comp_object_layers_remove(cw);
   /* prepend to client list since this client should be the first item now */
   _e_comp_object_layers_add(cw, NULL, NULL, 1);
   if (evas_object_layer_get(o) != evas_object_layer_get(obj)) return; //already at bottom!
   if (obj == e_comp->layers[cw->layer].obj) return; //never lower a layer marker!
   evas_object_data_set(obj, "client_restack", (void*)1);
   evas_object_lower(obj);
   evas_object_data_del(obj, "client_restack");
   if (!cw->visible) return;
   e_comp_render_queue();
   e_comp_shape_queue();
}

static void
_e_comp_intercept_raise(void *data, Evas_Object *obj)
{
   E_Comp_Object *cw = data;
   Evas_Object *o;

   if (cw->ec->layer_block)
     {
        evas_object_raise(obj);
        return;
     }
   if (!EINA_INLIST_GET(cw->ec)->next) return;//already highest on layer
   o = evas_object_above_get(obj);
   {
      E_Client *ecabove = e_client_above_get(cw->ec);
      if (ecabove && (ecabove->frame == e_comp->layers[cw->layer].obj) &&
          (ecabove->frame == o)) return; //highest below marker
   }
   if (evas_object_layer_get(o) != evas_object_layer_get(obj)) return; //already at top!
   if (obj == e_comp->layers[cw->layer].obj) //never raise a non-layer marker!
     evas_object_raise(obj);
   else
     {
        Evas_Object *op;

        /* still stack below override below the layer marker */
        for (op = o = e_comp->layers[cw->layer].obj;
             o && o != e_comp->layers[cw->layer - 1].obj;
             op = o, o = evas_object_below_get(o))
          {
             E_Client *ec;

             ec = e_comp_object_client_get(o);
             if (ec && (!ec->override)) break;
          }
        evas_object_stack_below(obj, op);
        if (e_client_focus_track_enabled())
          e_client_raise_latest_set(cw->ec); //modify raise list if necessary
     }
   if (!cw->visible) return;
   e_comp_render_queue();
   e_comp_shape_queue();
}

static void
_e_comp_intercept_hide(void *data, Evas_Object *obj)
{
   E_Comp_Object *cw = data;

   if (cw->ec->hidden)
     {
        /* hidden flag = just do it */
        evas_object_hide(obj);
        return;
     }

   if (cw->ec->input_only)
     {
        /* input_only = who cares */
        evas_object_hide(obj);
        return;
     }
   /* already hidden or currently animating */
   if ((!cw->visible) || (cw->animating && (!cw->showing) && (!cw->ec->iconic))) return;

   /* don't try hiding during shutdown */
   cw->defer_hide |= stopping;
   if (!cw->defer_hide)
     {
        if ((!cw->ec->iconic) && (!cw->ec->override))
          /* unset delete requested so the client doesn't break */
          cw->ec->delete_requested = 0;
        if ((!cw->animating) || cw->showing || cw->ec->iconic)
          {
             if (cw->ec->iconic)
               e_comp_object_signal_emit(obj, "e,action,iconify", "e");
             if ((!cw->ec->iconic) || (cw->ec->iconic && (!cw->animating)))
               {
                  e_comp_object_signal_emit(obj, "e,state,hidden", "e");
                  if (!cw->showing)
                    _e_comp_object_animating_begin(cw);
                  if (!_e_comp_object_effect_visibility_start(cw, 0)) return;
               }
             evas_object_smart_callback_call(obj, "hiding", cw->ec);
             cw->defer_hide = !!cw->animating;
             if (!cw->animating)
               e_comp_object_effect_set(obj, NULL);
          }
     }
   if (cw->animating) return;
   /* if we have no animations running, go ahead and hide */
   cw->defer_hide = 0;
   evas_object_hide(obj);
}

static void
_e_comp_intercept_show_helper(E_Comp_Object *cw)
{
   int w = 0, h = 0;

   if (cw->ec->sticky)
     e_comp_object_signal_emit(cw->smart_obj, "e,state,sticky", "e");
   if (cw->visible)
     {
        if (cw->ec->iconic && cw->animating)
          {
             /* triggered during iconify animation */
             e_comp_object_signal_emit(cw->smart_obj, "e,action,uniconify", "e");
             cw->defer_hide = 0;
          }
        return;
     }
   if ((!cw->updates) && (!cw->ec->input_only) && (!cw->ec->ignored))
     {
        _e_comp_object_updates_init(cw);
        if (!cw->updates)
          {
             cw->ec->changes.visible = !cw->ec->hidden;
             cw->ec->visible = 1;
             EC_CHANGED(cw->ec);
             return;
          }
     }
   if (cw->ec->new_client)
     {
        /* ignore until client idler first run */
        cw->ec->changes.visible = !cw->ec->hidden;
        cw->ec->visible = 1;
        EC_CHANGED(cw->ec);
        return;
     }
   /* ensure that some kind of frame calc has occurred if there's a frame */
   if (e_pixmap_is_x(cw->ec->pixmap) && cw->frame_object &&
       (cw->ec->h == cw->ec->client.h) && (cw->ec->w == cw->ec->client.w))
     ERR("Frame calc hasn't happened");
   /* force resize in case it hasn't happened yet, or just to update size */
   evas_object_resize(cw->smart_obj, cw->ec->w, cw->ec->h);
   if ((cw->w < 1) || (cw->h < 1))
     {
        /* if resize didn't go through, try again */
        cw->ec->visible = cw->ec->changes.visible = 1;
        EC_CHANGED(cw->ec);
        return;
     }
   /* re-set geometry */
   if (cw->ec->placed)
     evas_object_move(cw->smart_obj, cw->ec->x, cw->ec->y);

   /* if pixmap not available, clear pixmap since we're going to fetch it again */
   if (!e_pixmap_size_get(cw->ec->pixmap, &w, &h))
     e_pixmap_clear(cw->ec->pixmap);

   if (cw->real_hid && w && h)
     {
        DBG("  [%p] real hid - fix", cw->ec);
        cw->real_hid = 0;
        /* force comp theming in case it didn't happen already */
        e_comp_object_frame_theme_set(cw->smart_obj, E_COMP_OBJECT_FRAME_RESHADOW);
     }

   /* only do the show if show is allowed */
   if (!cw->real_hid)
     {
        if (cw->ec->internal) //internal clients render when they feel like it
          e_comp_object_damage(cw->smart_obj, 0, 0, cw->w, cw->h);
        evas_object_show(cw->smart_obj);
     }
}

static void
_e_comp_intercept_show(void *data, Evas_Object *obj EINA_UNUSED)
{
   E_Comp_Object *cw = data;
   E_Client *ec = cw->ec;

   if (ec->ignored) return;
   if (cw->effect_obj)
     {
        //INF("SHOW2 %p", ec);
        _e_comp_intercept_show_helper(cw);
        return;
     }
   //INF("SHOW %p", ec);
   if (ec->input_only)
     {
        cw->effect_obj = evas_object_rectangle_add(e_comp->evas);
        evas_object_color_set(cw->effect_obj, 0, 0, 0, 0);
        evas_object_smart_member_add(cw->effect_obj, cw->smart_obj);
     }
   else
     {
        _e_comp_object_setup(cw);
        cw->obj = evas_object_image_filled_add(e_comp->evas);
        evas_object_image_border_center_fill_set(cw->obj, EVAS_BORDER_FILL_SOLID);
        e_util_size_debug_set(cw->obj, 1);
        evas_object_image_pixels_get_callback_set(cw->obj, _e_comp_object_pixels_get, cw);
        evas_object_image_smooth_scale_set(cw->obj, e_comp_config_get()->smooth_windows);
        evas_object_name_set(cw->obj, "cw->obj");
        evas_object_image_colorspace_set(cw->obj, EVAS_COLORSPACE_ARGB8888);
        _e_comp_object_alpha_set(cw);

        if (cw->frame_object)
          {
             evas_object_event_callback_add(cw->obj, EVAS_CALLBACK_MOUSE_IN, _e_comp_object_ssd_mouse_in, cw);
             evas_object_event_callback_add(cw->obj, EVAS_CALLBACK_MOUSE_OUT, _e_comp_object_ssd_mouse_out, cw);
          }

#ifdef BORDER_ZOOMAPS
        e_comp_object_zoomap_set(o, 1);
#else
        cw->zoomap_disabled = 1;
#endif
        cw->redirected = 1;
        evas_object_color_set(cw->clip, ec->netwm.opacity, ec->netwm.opacity, ec->netwm.opacity, ec->netwm.opacity);
     }

   _e_comp_intercept_show_helper(cw);
}

static void
_e_comp_intercept_focus(void *data, Evas_Object *obj, Eina_Bool focus)
{
   E_Comp_Object *cw = data;
   E_Client *ec = cw->ec;

   if (focus)
     {
        ec = e_client_stack_active_adjust(ec);
        obj = ec->frame;
        cw = evas_object_data_get(obj, "comp_obj");
     }
   /* note: this is here as it seems there are enough apps that do not even
    * expect us to emulate a look of focus but not actually set x input
    * focus as we do - so simply abort any focus set on such windows */
   /* be strict about accepting focus hint */
   if (e_client_has_xwindow(ec))
     {
        /* be strict about accepting focus hint */
        if ((!ec->icccm.accepts_focus) &&
            (!ec->icccm.take_focus)) return;
     }
   if (focus && ec->lock_focus_out) return;
   if (e_object_is_del(E_OBJECT(ec)) && focus)
     {
        CRI("CAN'T FOCUS DELETED CLIENT!");
        return;
     }

   /* filter focus setting based on current state */
   if (focus)
     {
        if (ec->focused)
          {
             evas_object_focus_set(obj, focus);
             return;
          }
        if ((ec->iconic) && (!ec->deskshow))
          {
             /* don't focus an iconified window. that's silly! */
             e_client_uniconify(ec);
             if (e_client_focus_track_enabled())
               e_client_focus_latest_set(ec);
             return;
          }
        if (!ec->visible)
          {
             return;
          }
        if ((!ec->sticky) && (ec->desk) && (!ec->desk->visible))
          {
             if (ec->desk->animate_count) return;
             e_desk_show(ec->desk);
             if (!ec->desk->visible) return;
          }
     }

   if (focus)
     {
        /* check for dialog children that steal focus */
        if ((ec->modal) && (ec->modal != ec) &&
            (ec->modal->visible) && (!e_object_is_del(E_OBJECT(ec->modal))))
          {
             evas_object_focus_set(ec->modal->frame, focus);
             return;
          }
        else if ((ec->leader) && (ec->leader->modal) &&
                 (ec->leader->modal != ec) && ec->leader->modal->visible &&
                 (!e_object_is_del(E_OBJECT(ec->leader->modal))))
          {
             evas_object_focus_set(ec->leader->modal->frame, focus);
             return;
          }
        if (!cw->visible)
          {
             /* not yet visible, wait till the next time... */
             ec->want_focus = !ec->hidden;
             if (ec->want_focus)
               EC_CHANGED(ec);
             return;
          }
        e_client_focused_set(ec);
     }
   else
     {
        if (e_client_focused_get() == ec)
          e_client_focused_set(NULL);
     }
   evas_object_focus_set(obj, focus);
}

////////////////////////////////////////////////////

static void
_e_comp_object_frame_recalc(E_Comp_Object *cw)
{
   int w, h, ox, oy, ow, oh;

   if (cw->frame_object)
     {
        if (cw->obj) edje_object_part_unswallow(cw->frame_object, cw->obj);
        evas_object_geometry_get(cw->frame_object, NULL, NULL, &w, &h);
        /* set a fixed size, force edje calc, check size difference */
        evas_object_resize(cw->frame_object, MAX(w, 50), MAX(h, 50));
        edje_object_message_signal_process(cw->frame_object);
        edje_object_calc_force(cw->frame_object);
        edje_object_part_geometry_get(cw->frame_object, "e.swallow.client", &ox, &oy, &ow, &oh);
        cw->client_inset.l = ox;
        cw->client_inset.r = MAX(w, 50) - (ox + ow);
        cw->client_inset.t = oy;
        cw->client_inset.b = MAX(h, 50) - (oy + oh);
        if (cw->obj) edje_object_part_swallow(cw->frame_object, "e.swallow.client", cw->obj);
        evas_object_resize(cw->frame_object, w, h);
        if (!cw->input_objs)
          evas_object_pass_events_set(cw->obj, 0);
     }
   else
     {
        cw->client_inset.l = 0;
        cw->client_inset.r = 0;
        cw->client_inset.t = 0;
        cw->client_inset.b = 0;
     }
   cw->client_inset.calc = !!cw->frame_object;
}

static void
_e_comp_smart_cb_frame_recalc(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;
   int w = 0, h = 0, pw = 0, ph = 0;

   /* - get current size
    * - calc new size
    * - readjust for new frame size
    */

   w = cw->ec->w, h = cw->ec->h;
   e_comp_object_frame_wh_unadjust(obj, w, h, &pw, &ph);

   _e_comp_object_frame_recalc(cw);

   if (!cw->ec->fullscreen)
     e_comp_object_frame_wh_adjust(obj, cw->ec->client.w, cw->ec->client.h, &w, &h);

   evas_object_smart_callback_call(cw->smart_obj, "frame_recalc_done", &cw->client_inset);
   if (cw->ec->shading || cw->ec->shaded) return;
   if (cw->ec->fullscreen)
     evas_object_resize(cw->ec->frame, cw->ec->zone->w, cw->ec->zone->h);
   else if (cw->ec->new_client)
     {
        if ((cw->ec->w < 1) || (cw->ec->h < 1)) return;
        e_comp_object_frame_wh_adjust(obj, pw, ph, &w, &h);
        evas_object_resize(cw->ec->frame, w, h);
     }
   else if ((w != cw->ec->w) || (h != cw->ec->h))
     evas_object_resize(cw->ec->frame, w, h);
}

static Eina_Bool
_e_comp_object_shade_animator(void *data)
{
   E_Comp_Object *cw = data;
   Eina_Bool move = EINA_FALSE;
   int x, y, w, h;
   double dt, val;
   double dur;

   dt = ecore_loop_time_get() - cw->shade.start;
   dur = cw->ec->client.h / e_config->border_shade_speed;
   val = dt / dur;

   if (val < 0.0)
     val = 0.0;
   else if (val > 1.0)
     val = 1.0;

   if (e_config->border_shade_transition == E_TRANSITION_SINUSOIDAL)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_SINUSOIDAL, 0.0, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_DECELERATE)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_DECELERATE, 0.0, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_ACCELERATE)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_ACCELERATE, 0.0, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_LINEAR)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_LINEAR, 0.0, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_ACCELERATE_LOTS)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_ACCELERATE_FACTOR, 1.7, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_DECELERATE_LOTS)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_DECELERATE_FACTOR, 1.7, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_SINUSOIDAL_LOTS)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_SINUSOIDAL_FACTOR, 1.7, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_BOUNCE)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_BOUNCE, 1.2, 3.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else if (e_config->border_shade_transition == E_TRANSITION_BOUNCE_LOTS)
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_BOUNCE, 1.2, 5.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }
   else
     {
        cw->shade.val =
          ecore_animator_pos_map(val, ECORE_POS_MAP_LINEAR, 0.0, 0.0);
        if (!cw->ec->shaded) cw->shade.val = 1.0 - cw->shade.val;
     }

   /* due to M_PI's inaccuracy, cos(M_PI/2) != 0.0, so we need this */
   if (cw->shade.val < 0.001) cw->shade.val = 0.0;
   else if (cw->shade.val > .999)
     cw->shade.val = 1.0;

   x = cw->ec->x, y = cw->ec->y, w = cw->ec->w, h = cw->ec->h;
   if (cw->shade.dir == E_DIRECTION_UP)
     h = cw->client_inset.t + cw->ec->client.h * cw->shade.val;
   else if (cw->shade.dir == E_DIRECTION_DOWN)
     {
        h = cw->client_inset.t + cw->ec->client.h * cw->shade.val;
        y = cw->shade.y + cw->ec->client.h * (1 - cw->shade.val);
        move = EINA_TRUE;
     }
   else if (cw->shade.dir == E_DIRECTION_LEFT)
     w = cw->client_inset.t + cw->ec->client.w * cw->shade.val;
   else if (cw->shade.dir == E_DIRECTION_RIGHT)
     {
        w = cw->client_inset.t + cw->ec->client.w * cw->shade.val;
        x = cw->shade.x + cw->ec->client.w * (1 - cw->shade.val);
        move = EINA_TRUE;
     }

   if (move) evas_object_move(cw->smart_obj, x, y);
   evas_object_resize(cw->smart_obj, w, h);

   /* we're done */
   if (EINA_DBL_EQ(val, 1))
     {
        cw->shade.anim = NULL;

        evas_object_smart_callback_call(cw->smart_obj, "shade_done", NULL);
        if (cw->ec->shaded)
          e_comp_object_signal_emit(cw->smart_obj, "e,state,shaded", "e");
        else
          e_comp_object_signal_emit(cw->smart_obj, "e,state,unshaded", "e");
        edje_object_message_signal_process(cw->frame_object);
        _e_comp_smart_cb_frame_recalc(cw, cw->smart_obj, NULL);
     }
   return cw->ec->shading;
}

static void
_e_comp_smart_cb_shaded(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   E_FREE_FUNC(cw->shade.anim, ecore_animator_del);

   e_comp_object_signal_emit(cw->smart_obj, "e,state,shaded", "e");
   cw->shade.start = -100;
   cw->shade.dir = (E_Direction)event_info;
   _e_comp_object_shade_animator(cw);
}

static void
_e_comp_smart_cb_shading(void *data, Evas_Object *obj, void *event_info)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   if (cw->shade.anim && EINA_DBL_EQ(cw->shade.val, 0.0))
     {
        cw->ec->shaded = 0;
        _e_comp_smart_cb_shaded(data, obj, event_info);
        return;
     }
   E_FREE_FUNC(cw->shade.anim, ecore_animator_del);

   cw->shade.x = cw->x;
   cw->shade.y = cw->y;
   e_comp_object_signal_emit(cw->smart_obj, "e,state,shading", "e");
   cw->shade.start = ecore_loop_time_get();
   cw->shade.dir = (E_Direction)event_info;
   cw->shade.anim = ecore_animator_add(_e_comp_object_shade_animator, cw);
}

static void
_e_comp_smart_cb_unshaded(void *data, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   E_FREE_FUNC(cw->shade.anim, ecore_animator_del);

   cw->shade.dir = (E_Direction)event_info;
   if (cw->shade.dir == E_DIRECTION_UP ||
       cw->shade.dir == E_DIRECTION_LEFT)
     {
        cw->shade.x = cw->x;
        cw->shade.y = cw->y;
     }
   else
     {
        cw->shade.x = cw->x - cw->w;
        cw->shade.y = cw->y - cw->h;
     }
   e_comp_object_signal_emit(cw->smart_obj, "e,state,unshaded", "e");
   cw->shade.start = -100;
   _e_comp_object_shade_animator(cw);
}

static void
_e_comp_smart_cb_unshading(void *data, Evas_Object *obj, void *event_info)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   if (cw->shade.anim && EINA_DBL_EQ(cw->shade.val, 0.0))
     {
        cw->ec->shaded = 1;
        _e_comp_smart_cb_unshaded(data, obj, event_info);
        return;
     }
   E_FREE_FUNC(cw->shade.anim, ecore_animator_del);

   cw->shade.dir = (E_Direction)event_info;
   e_comp_object_signal_emit(cw->smart_obj, "e,state,unshading", "e");
   cw->shade.start = ecore_loop_time_get();
   cw->shade.anim = ecore_animator_add(_e_comp_object_shade_animator, cw);
}

static void
_e_comp_smart_cb_maximize(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   _e_comp_object_shadow_setup(cw);
   if (cw->frame_object)
     {
        _e_comp_object_shadow(cw);
        e_comp_object_signal_emit(cw->smart_obj, "e,action,maximize", "e");
        _e_comp_object_frame_recalc(cw);
        evas_object_smart_callback_call(cw->smart_obj, "frame_recalc_done", &cw->client_inset);
     }
}

static void
_e_comp_smart_cb_fullscreen(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if (_e_comp_object_shadow_setup(cw))
     e_comp_object_damage(cw->smart_obj, 0, 0, cw->ec->w, cw->ec->h);
   if (cw->frame_object)
     {
        _e_comp_object_shadow(cw);
        e_comp_object_signal_emit(cw->smart_obj, "e,action,maximize,fullscreen", "e");
        _e_comp_object_frame_recalc(cw);
        evas_object_smart_callback_call(cw->smart_obj, "frame_recalc_done", &cw->client_inset);
     }
}

static void
_e_comp_smart_cb_unmaximize(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if (cw->frame_object)
     {
        _e_comp_object_shadow(cw);
        e_comp_object_signal_emit(obj, "e,action,unmaximize", "e");
        _e_comp_object_frame_recalc(cw);
        evas_object_smart_callback_call(cw->smart_obj, "frame_recalc_done", &cw->client_inset);
     }
}

static void
_e_comp_smart_cb_unfullscreen(void *data, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if (_e_comp_object_shadow_setup(cw))
     {
        EC_CHANGED(cw->ec);
        cw->ec->changes.size = 1;
     }
   if (cw->frame_object)
     {
        _e_comp_object_shadow(cw);
        e_comp_object_signal_emit(obj, "e,action,unmaximize,unfullscreen", "e");
        _e_comp_object_frame_recalc(cw);
        evas_object_smart_callback_call(cw->smart_obj, "frame_recalc_done", &cw->client_inset);
     }
}

static void
_e_comp_smart_cb_sticky(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   e_comp_object_signal_emit(obj, "e,state,sticky", "e");
}

static void
_e_comp_smart_cb_unsticky(void *data EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   e_comp_object_signal_emit(obj, "e,state,unsticky", "e");
}

static void
_e_comp_smart_cb_unhung(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   e_comp_object_signal_emit(cw->smart_obj, "e,state,unhung", "e");
}

static void
_e_comp_smart_cb_hung(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if (!cw->ec) return; //NYI
   e_comp_object_signal_emit(cw->smart_obj, "e,state,hung", "e");
}

static void
_e_comp_smart_focus_in(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   e_comp_object_signal_emit(obj, "e,state,focused", "e");
}

static void
_e_comp_smart_focus_out(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   E_Comp_Object *cw = data;

   if ((!cw->deleted) && (!e_object_is_del(E_OBJECT(cw->ec))))
     e_comp_object_signal_emit(obj, "e,state,unfocused", "e");
}

static void
_e_comp_smart_add(Evas_Object *obj)
{
   E_Comp_Object *cw;

   cw = E_NEW(E_Comp_Object, 1);
   cw->smart_obj = obj;
   cw->x = cw->y = cw->w = cw->h = -1;
   evas_object_smart_data_set(obj, cw);
   cw->opacity = 255.0;
   evas_object_data_set(obj, "comp_obj", cw);
   evas_object_move(obj, -1, -1);
   /* intercept ALL the callbacks! */
   evas_object_intercept_stack_above_callback_add(obj, _e_comp_intercept_stack_above, cw);
   evas_object_intercept_stack_below_callback_add(obj, _e_comp_intercept_stack_below, cw);
   evas_object_intercept_raise_callback_add(obj, _e_comp_intercept_raise, cw);
   evas_object_intercept_lower_callback_add(obj, _e_comp_intercept_lower, cw);
   evas_object_intercept_layer_set_callback_add(obj, _e_comp_intercept_layer_set, cw);
   evas_object_intercept_move_callback_add(obj, _e_comp_intercept_move, cw);
   evas_object_intercept_resize_callback_add(obj, _e_comp_intercept_resize, cw);
   evas_object_intercept_show_callback_add(obj, _e_comp_intercept_show, cw);
   evas_object_intercept_hide_callback_add(obj, _e_comp_intercept_hide, cw);
   evas_object_intercept_focus_set_callback_add(obj, _e_comp_intercept_focus, cw);

   evas_object_smart_callback_add(obj, "shading", _e_comp_smart_cb_shading, cw);
   evas_object_smart_callback_add(obj, "shaded", _e_comp_smart_cb_shaded, cw);
   evas_object_smart_callback_add(obj, "unshading", _e_comp_smart_cb_unshading, cw);
   evas_object_smart_callback_add(obj, "unshaded", _e_comp_smart_cb_unshaded, cw);

   evas_object_smart_callback_add(obj, "maximize", _e_comp_smart_cb_maximize, cw);
   evas_object_smart_callback_add(obj, "fullscreen", _e_comp_smart_cb_fullscreen, cw);
   evas_object_smart_callback_add(obj, "unmaximize", _e_comp_smart_cb_unmaximize, cw);
   evas_object_smart_callback_add(obj, "unfullscreen", _e_comp_smart_cb_unfullscreen, cw);

   evas_object_smart_callback_add(obj, "stick", _e_comp_smart_cb_sticky, cw);
   evas_object_smart_callback_add(obj, "unstick", _e_comp_smart_cb_unsticky, cw);

   evas_object_smart_callback_add(obj, "hung", _e_comp_smart_cb_hung, cw);
   evas_object_smart_callback_add(obj, "unhung", _e_comp_smart_cb_unhung, cw);

   evas_object_smart_callback_add(obj, "frame_recalc", _e_comp_smart_cb_frame_recalc, cw);

   evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_IN, _e_comp_smart_focus_in, cw);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_FOCUS_OUT, _e_comp_smart_focus_out, cw);
}

static void
_e_comp_smart_color_set(Evas_Object *obj, int r, int g, int b, int a)
{
   INTERNAL_ENTRY;
   evas_object_color_set(cw->clip, r, g, b, a);
   cw->ec->netwm.opacity = a;
   if (cw->ec->remember && (cw->ec->remember->apply & E_REMEMBER_APPLY_OPACITY))
     e_remember_update(cw->ec);
   evas_object_smart_callback_call(obj, "color_set", NULL);
}


static void
_e_comp_smart_clip_set(Evas_Object *obj, Evas_Object *clip)
{
   INTERNAL_ENTRY;
   evas_object_clip_set(cw->clip, clip);
}

static void
_e_comp_smart_clip_unset(Evas_Object *obj)
{
   INTERNAL_ENTRY;
   evas_object_clip_unset(cw->clip);
}

static void
_e_comp_smart_hide(Evas_Object *obj)
{
   INTERNAL_ENTRY;
   cw->visible = 0;
   cw->deleted |= cw->ec->delete_requested || e_object_is_del(E_OBJECT(cw->ec));
   evas_object_hide(cw->clip);
   evas_object_hide(cw->effect_obj);
   if (stopping) return;
   if (!cw->ec->input_only)
     {
        edje_object_freeze(cw->effect_obj);
        edje_object_freeze(cw->shobj);
        edje_object_play_set(cw->shobj, 0);
        if (cw->frame_object)
          edje_object_play_set(cw->frame_object, 0);
     }
   /* ensure focus-out */
   if (cw->ec->focused)
     evas_object_focus_set(cw->ec->frame, 0);
   e_comp_render_queue(); //force nocomp recheck
   e_comp_shape_queue();
}

static void
_e_comp_smart_show(Evas_Object *obj)
{
   E_Client *tmp;
   Eina_List *l;

   INTERNAL_ENTRY;
   cw->defer_hide = 0;
   cw->visible = 1;
   if ((cw->w < 0) || (cw->h < 0))
     CRI("ACK!");

   //INF("SMART SHOW: %p EC(%dx%d) CW(%dx%d)", cw->ec, cw->ec->w, cw->ec->h, cw->w, cw->h);

   EINA_LIST_FOREACH(cw->ec->e.state.video_child, l, tmp)
     evas_object_show(tmp->frame);

   evas_object_show(cw->clip);
   if (!cw->ec->input_only)
     {
        edje_object_thaw(cw->effect_obj);
        edje_object_thaw(cw->shobj);
        edje_object_play_set(cw->shobj, 1);
        if (cw->frame_object)
          edje_object_play_set(cw->frame_object, 1);
     }
   evas_object_show(cw->effect_obj);
   if (cw->ec->internal_elm_win && (!evas_object_visible_get(cw->ec->internal_elm_win)))
     evas_object_show(cw->ec->internal_elm_win);
   e_comp_render_queue();
   if (cw->ec->input_only)
     {
        e_comp_shape_queue();
        return;
     }
   if (cw->ec->iconic && (!cw->ec->new_client))
     e_comp_object_signal_emit(cw->smart_obj, "e,action,uniconify", "e");
   if (cw->updates_exist)
     e_comp_object_render_update_add(obj);
   if ((!cw->showing) && ((!cw->ec->iconic) || cw->ec->new_client || (!cw->animating)))
     {
        /* if cw->showing set, client was ec->hidden during show animation */
        e_comp_object_signal_emit(cw->smart_obj, "e,state,visible", "e");
        _e_comp_object_animating_begin(cw);
        cw->showing = 1;
        if (!_e_comp_object_effect_visibility_start(cw, 1)) return;
     }
   /* ensure some random effect doesn't lock the client offscreen */
   if (!cw->animating)
     {
        cw->showing = 0;
        e_comp_object_effect_set(obj, NULL);
        e_comp_shape_queue();
     }
}

static void
_e_comp_object_client_del(void *d, void *obj EINA_UNUSED)
{
   E_Comp_Object *cw = d;
   cw->deleted = 1;
   e_comp_object_render_update_del(cw->smart_obj);
   _e_comp_object_layers_remove(cw);
}

static void
_e_comp_input_objs_free(E_Comp_Object *cw)
{
   Evas_Object *rect;
   if (!cw->input_objs) return;
   while ((rect = eina_array_pop(cw->input_objs)))
     {
        evas_object_smart_member_del(rect);
        evas_object_freeze_events_set(rect, 1);
        evas_object_del(rect);
     }
}

static void
_e_comp_smart_del(Evas_Object *obj)
{
   Eina_List *l;
   Evas_Object *o;

   INTERNAL_ENTRY;

   E_FREE_FUNC(cw->updates, eina_tiler_free);
   E_FREE_FUNC(cw->pending_updates, eina_tiler_free);
   free(cw->ns);

   EINA_LIST_FREE(cw->obj_mirror, o)
     {
        evas_object_image_data_set(o, NULL);
        evas_object_freeze_events_set(o, 1);
        evas_object_event_callback_del_full(o, EVAS_CALLBACK_DEL, _e_comp_object_cb_mirror_del, cw);
        evas_object_event_callback_del_full(o, EVAS_CALLBACK_SHOW, _e_comp_object_cb_mirror_show, cw);
        evas_object_event_callback_del_full(o, EVAS_CALLBACK_HIDE, _e_comp_object_cb_mirror_hide, cw);
        evas_object_del(o);
     }
   EINA_LIST_FREE(cw->obj_agent, o)
     {
        evas_object_freeze_events_set(o, 1);
        evas_object_event_callback_del_full(o, EVAS_CALLBACK_DEL, _e_comp_object_cb_agent_del, cw);
        evas_object_del(o);
     }
   if (!cw->deleted)
     _e_comp_object_layers_remove(cw);
   l = evas_object_data_get(obj, "comp_object-to_del");
   E_FREE_LIST(l, evas_object_del);
   evas_object_del(cw->clip);
   evas_object_del(cw->effect_obj);
   evas_object_del(cw->shobj);
   evas_object_del(cw->frame_icon);
   evas_object_del(cw->frame_volume);
   evas_object_del(cw->frame_object);
   evas_object_del(cw->zoomobj);
   _e_comp_input_objs_free(cw);
   eina_array_free(cw->input_objs);
   eina_tiler_free(cw->input_area);
   evas_object_del(cw->obj);
   e_comp_shape_queue();
   eina_stringshare_del(cw->frame_theme);
   eina_stringshare_del(cw->frame_name);
   if (cw->animating)
     {
        cw->animating = 0;
        e_comp->animating--;
        UNREFD(cw->ec, 2);
        e_object_unref(E_OBJECT(cw->ec));
     }
//   UNREFD(cw->ec, 9);
//   e_object_unref(E_OBJECT(cw->ec));
   free(cw);
}

static void
_e_comp_object_input_rect_update(E_Comp_Object *cw)
{
   int x, y, w, h;
   Eina_Iterator *it;
   Eina_Rectangle *rect;
   Evas_Object *o;
   int i = 0;

   if (!cw->input_area) return;
   it = eina_tiler_iterator_new(cw->input_area);
   EINA_ITERATOR_FOREACH(it, rect)
     {
        o = eina_array_data_get(cw->input_objs, i++);
        x = rect->x, y = rect->y, w = rect->w, h = rect->h;

        E_RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0,
                             cw->ec->client.w, cw->ec->client.h);
        evas_object_geometry_set
          (o, cw->x + x + (!!cw->frame_object * cw->client_inset.l),
              cw->y + y + (!!cw->frame_object * cw->client_inset.t),
              w, h);
     }
   eina_iterator_free(it);
}

static void
_e_comp_smart_move(Evas_Object *obj, int x, int y)
{
   Eina_List *l;
   Evas_Object *o;

   INTERNAL_ENTRY;

   cw->x = x, cw->y = y;
   cw->agent_updating = 1;
   EINA_LIST_FOREACH(cw->obj_agent, l, o)
     evas_object_move(o, cw->ec->x, cw->ec->x);
   cw->agent_updating = 0;
   if (cw->clip) evas_object_move(cw->clip, 0, 0);
   if (cw->effect_obj) evas_object_move(cw->effect_obj, x, y);
   if (cw->input_area)
     _e_comp_object_input_rect_update(cw);
   /* this gets called once during setup to init coords offscreen and guarantee first move */
   if (e_comp && cw->visible)
     e_comp_shape_queue();
}

static void
_e_comp_smart_resize(Evas_Object *obj, int w, int h)
{
   Eina_Bool first = EINA_FALSE;
   Eina_List *l;
   Evas_Object *o;

   INTERNAL_ENTRY;

   //INF("RSZ(%p): %dx%d -> %dx%d", cw->ec, cw->w, cw->h, w, h);
   if (!cw->effect_obj) CRI("ACK!");
   first = ((cw->w < 1) || (cw->h < 1));
   cw->w = w, cw->h = h;
   if ((!cw->ec->shading) && (!cw->ec->shaded))
     {
        int ww = 0, hh = 0, pw = 0, ph = 0;

        if (cw->frame_object)
          e_comp_object_frame_wh_unadjust(obj, w, h, &ww, &hh);
        else
          {
             ww = w;
             hh = h;
          }
        /* verify pixmap:object size */
        if (e_pixmap_size_get(cw->ec->pixmap, &pw, &ph) && (!cw->ec->override))
          {
             //INF("CW RSZ: %dx%d PIX(%dx%d)", w, h, pw, ph);
             //if (cw->obj)
               //{
                  //evas_object_size_hint_max_set(cw->obj, pw, ph);
                  //evas_object_size_hint_min_set(cw->obj, pw, ph);
               //}
             if ((ww != pw) || (hh != ph))
               CRI("CW RSZ: %dx%d || PX: %dx%d", ww, hh, pw, ph);
          }
        evas_object_resize(cw->effect_obj, w, h);
        if (cw->zoomobj) e_zoomap_child_resize(cw->zoomobj, pw, ph);
        if (cw->input_area)
          _e_comp_object_input_rect_update(cw);
        /* resize render update tiler */
        if (!first)
          {
             if (cw->updates)
               {
                  int tw, th;

                  eina_tiler_area_size_get(cw->updates, &tw, &th);
                  if ((tw != pw) || (th != ph))
                    {
                       RENDER_DEBUG("DAMAGE UNFULL: %p", cw->ec);
                       cw->updates_full = 0;
                       eina_tiler_clear(cw->updates);
                    }
               }
          }
        else
          {
             RENDER_DEBUG("DAMAGE RESIZE(%p): %dx%d", cw->ec, cw->ec->client.w, cw->ec->client.h);
             if (cw->updates) eina_tiler_area_size_set(cw->updates, cw->ec->client.w, cw->ec->client.h);
          }
     }
   else
     {
        evas_object_resize(cw->effect_obj, w, h);
     }
   cw->agent_updating = 1;
   EINA_LIST_FOREACH(cw->obj_agent, l, o)
     evas_object_resize(o, cw->ec->w, cw->ec->h);
   cw->agent_updating = 0;
   if (!cw->visible) return;
   e_comp_render_queue();
   if (!cw->animating)
     e_comp_shape_queue();
}

static void
_e_comp_object_move_end(void *d EINA_UNUSED, E_Client *ec)
{
   E_Comp_Object *cw = evas_object_smart_data_get(ec->frame);
   unsigned int i;
   Evas_Object *rect;
   Eina_Array_Iterator it;

   if (!cw) return;
   if (!cw->input_objs) return;

   EINA_ARRAY_ITER_NEXT(cw->input_objs, i, rect, it)
     evas_object_pointer_mode_set(rect, EVAS_OBJECT_POINTER_MODE_AUTOGRAB);
}

static void
_e_comp_object_move_begin(void *d EINA_UNUSED, E_Client *ec)
{
   E_Comp_Object *cw = evas_object_smart_data_get(ec->frame);
   unsigned int i;
   Evas_Object *rect;
   Eina_Array_Iterator it;

   if (!cw) return;
   if (!cw->input_objs) return;

   EINA_ARRAY_ITER_NEXT(cw->input_objs, i, rect, it)
     evas_object_pointer_mode_set(rect, EVAS_OBJECT_POINTER_MODE_NOGRAB);
}

static void
_e_comp_smart_init(void)
{
   const char *env;
   if (_e_comp_smart) return;

   env = getenv("E_RENDER_DEBUG");
   if (eina_streq(env, "focus"))
     render_debug_enabled = -1;
   else if (env)
     render_debug_enabled = 1;
   e_client_hook_add(E_CLIENT_HOOK_MOVE_BEGIN, _e_comp_object_move_begin, NULL);
   e_client_hook_add(E_CLIENT_HOOK_MOVE_END, _e_comp_object_move_end, NULL);
   {
      static const Evas_Smart_Class sc =
      {
         SMART_NAME,
         EVAS_SMART_CLASS_VERSION,
         _e_comp_smart_add,
         _e_comp_smart_del,
         _e_comp_smart_move,
         _e_comp_smart_resize,
         _e_comp_smart_show,
         _e_comp_smart_hide,
         _e_comp_smart_color_set,
         _e_comp_smart_clip_set,
         _e_comp_smart_clip_unset,
         NULL,
         NULL,
         NULL,

         NULL,
         NULL,
         NULL,
         NULL
      };
      _e_comp_smart = evas_smart_class_new(&sc);
   }
}

E_API void
e_comp_object_zoomap_set(Evas_Object *obj, Eina_Bool enabled)
{
   API_ENTRY;

   enabled = !enabled;
   if (cw->zoomap_disabled == enabled) return;
   if (enabled)
     {
        cw->zoomobj = e_zoomap_add(e_comp->evas);
        e_zoomap_smooth_set(cw->zoomobj, e_comp_config_get()->smooth_windows);
        e_zoomap_child_set(cw->zoomobj, cw->ec ? cw->frame_object : cw->obj);
        edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->zoomobj);
        e_zoomap_child_edje_solid_setup(cw->zoomobj);
        if (cw->ec->override)
          evas_object_name_set(cw->zoomobj, "cw->zoomobj::WINDOW");
        else if (!cw->ec->input_only)
          evas_object_name_set(cw->zoomobj, "cw->zoomobj::CLIENT");
     }
   else
     {
        edje_object_part_unswallow(cw->shobj, cw->zoomobj);
        E_FREE_FUNC(cw->zoomobj, evas_object_del);
        edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->ec ? cw->frame_object : cw->obj);
     }
   cw->zoomap_disabled = enabled;
}

E_API Eina_Bool
e_comp_object_mirror_visibility_check(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;
   return !!cw->force_visible;
}
/////////////////////////////////////////////////////////

static void
_e_comp_object_util_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   Eina_List *l;
   Eina_Bool comp_object;

   comp_object = !!evas_object_data_get(obj, "comp_object");
   if (comp_object)
     {
        Evas_Object *o;

        o = edje_object_part_swallow_get(obj, "e.swallow.content");
        evas_object_del(o);
        e_comp_render_queue();
        e_comp_shape_queue();
     }
   l = evas_object_data_get(obj, "comp_object-to_del");
   E_FREE_LIST(l, evas_object_del);
}

static void
_e_comp_object_util_restack(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   if (e_comp_util_object_is_above_nocomp(obj) &&
       (!evas_object_data_get(obj, "comp_override")))
     {
        evas_object_data_set(obj, "comp_override", (void*)1);
        e_comp_override_add();
     }
}

static void
_e_comp_object_util_show(void *data EINA_UNUSED, Evas_Object *obj)
{
   Eina_Bool ref = EINA_TRUE;
   if (evas_object_visible_get(obj))
     {
        void *d;

        d = evas_object_data_del(obj, "comp_hiding");
        if (d)
          /* currently trying to hide */
          ref = EINA_FALSE;
        else
          /* already visible */
          return;
     }
   else
     e_comp_shape_queue();

   evas_object_show(obj);
   if (ref && (!stopping))
     {
        evas_object_ref(obj);
        evas_object_data_set(obj, "comp_ref", (void*)1);
     }
   edje_object_signal_emit(obj, "e,state,visible", "e");
   edje_object_message_signal_process(obj);
   evas_object_data_set(obj, "comp_showing", (void*)1);
   if (e_comp_util_object_is_above_nocomp(obj))
     {
        evas_object_data_set(obj, "comp_override", (void*)1);
        e_comp_override_add();
     }
}

static void
_e_comp_object_util_hide(void *data EINA_UNUSED, Evas_Object *obj)
{
   if (!evas_object_visible_get(obj)) return;
   /* already hiding */
   if (evas_object_data_get(obj, "comp_hiding"))
     {
        evas_object_data_del(obj, "comp_hiding");
        evas_object_hide(obj);
        e_comp_shape_queue();
        if (evas_object_data_del(obj, "comp_ref"))
          evas_object_unref(obj);
        return;
     }
   if ((!stopping) && (!evas_object_data_del(obj, "comp_showing")))
     {
        evas_object_ref(obj);
        evas_object_data_set(obj, "comp_ref", (void*)1);
     }
   edje_object_signal_emit(obj, "e,state,hidden", "e");
   edje_object_message_signal_process(obj);
   evas_object_data_set(obj, "comp_hiding", (void*)1);

   if (evas_object_data_del(obj, "comp_override"))
     e_comp_override_timed_pop();
}

static void
_e_comp_object_util_done_defer(void *data, Evas_Object *obj, const char *emission, const char *source EINA_UNUSED)
{
   if (!e_util_strcmp(emission, "e,action,hide,done"))
     {
        if (!evas_object_data_del(obj, "comp_hiding")) return;
        evas_object_intercept_hide_callback_del(obj, _e_comp_object_util_hide);
        evas_object_hide(obj);
        e_comp_shape_queue();
        evas_object_intercept_hide_callback_add(obj, _e_comp_object_util_hide, data);
     }
   else
     evas_object_data_del(obj, "comp_showing");
   if (evas_object_data_del(obj, "comp_ref"))
     evas_object_unref(obj);
}

static void
_e_comp_object_util_moveresize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   if (data)
     {
        int w, h;

        evas_object_geometry_get(obj, NULL, NULL, &w, &h);
        e_zoomap_child_resize(data, w, h);
     }
   if (evas_object_visible_get(obj))
     e_comp_shape_queue();
}

E_API void
e_comp_object_util_type_set(Evas_Object *obj, E_Comp_Object_Type type)
{
   Evas_Object *content;
   const char *name = NULL;
   Eina_List *l, *list = NULL;
   E_Comp_Config *conf = e_comp_config_get();
   Eina_Bool skip = EINA_FALSE, shadow = EINA_FALSE;
   E_Comp_Match *m;
   const char *grp;
   char buf[1024];
   int ok = 0;

   EINA_SAFETY_ON_NULL_RETURN(obj);

   edje_object_file_get(obj, NULL, &grp);
   switch (type)
     {
      case E_COMP_OBJECT_TYPE_MENU:
        list = conf->match.menus;
        shadow = EINA_TRUE;
        break;
      case E_COMP_OBJECT_TYPE_POPUP:
        list = conf->match.popups;
        break;
      default:
        list = conf->match.objects;
     }
   content = edje_object_part_swallow_get(obj, "e.swallow.content");
   if (content)
     {
        if (eina_streq(evas_object_type_get(content), "e_zoomap"))
          name = evas_object_name_get(e_zoomap_child_get(content));
        else
          name = evas_object_name_get(content);
     }
   if (name && (!skip))
     skip = (!strncmp(name, "noshadow", 8));
   if (skip)
     evas_object_data_set(obj, "comp_object_skip", (void*)1);
   else
     evas_object_data_del(obj, "comp_object_skip");
   if (list && (!skip))
     {
        EINA_LIST_FOREACH(list, l, m)
          {
             if (((m->name) && (!name)) ||
                 ((name) && (m->name) && (!e_util_glob_match(name, m->name))))
               continue;
             if (!m->shadow_style) continue;
             snprintf(buf, sizeof(buf), "e/comp/frame/%s", m->shadow_style);
             if (eina_streq(buf, grp)) return;
             ok = e_theme_edje_object_set(obj, "base/theme/comp", buf);
             if (ok)
               {
                  shadow = !m->no_shadow;
                  break;
               }
          }
     }
   else
     skip = EINA_TRUE;
   while (!ok)
     {
        if (skip)
          {
             if (eina_streq("e/comp/frame/none", grp)) return;
             ok = e_theme_edje_object_set(obj, "base/theme/comp", "e/comp/frame/none");
          }
        if (ok) break;
        if (conf->shadow_style)
          {
             snprintf(buf, sizeof(buf), "e/comp/frame/%s", conf->shadow_style);
             if (eina_streq(buf, grp)) return;
             ok = e_theme_edje_object_set(obj, "base/theme/comp", buf);
             if (ok) break;
          }
        if (eina_streq("e/comp/frame/default", grp)) return;
        ok = e_theme_edje_object_set(obj, "base/theme/comp", "e/comp/frame/default");
        break;
     }
   if (shadow && (e_util_strcmp(evas_object_type_get(obj), "edje") || (!edje_object_data_get(obj, "noshadow"))))
     edje_object_signal_emit(obj, "e,state,shadow,on", "e");
   else
     edje_object_signal_emit(obj, "e,state,shadow,off", "e");
   if (evas_object_visible_get(obj))
     edje_object_signal_emit(obj, "e,state,visible", "e");
   if (content)
     edje_object_part_swallow(obj, "e.swallow.content", content);
}

E_API Evas_Object *
e_comp_object_util_add(Evas_Object *obj, E_Comp_Object_Type type)
{
   Evas_Object *o, *z = NULL;
   E_Comp_Config *conf = e_comp_config_get();
   int x, y, w, h;
   Eina_Bool vis;

   EINA_SAFETY_ON_NULL_RETURN_VAL(obj, NULL);
   vis = evas_object_visible_get(obj);
   o = edje_object_add(e_comp->evas);
   evas_object_data_set(o, "comp_object", (void*)1);
   e_comp_object_util_type_set(o, type);

   evas_object_geometry_get(obj, &x, &y, &w, &h);
   evas_object_geometry_set(o, x, y, w, h);
   evas_object_pass_events_set(o, evas_object_pass_events_get(obj));

   z = e_zoomap_add(e_comp->evas);
   evas_object_show(z);
   evas_object_geometry_set(z, x, y, w, h);
   e_zoomap_child_edje_solid_setup(z);
   e_zoomap_smooth_set(z, conf->smooth_windows);
   e_zoomap_child_set(z, obj);
   edje_object_signal_callback_add(o, "e,action,*,done", "e", _e_comp_object_util_done_defer, z);

   evas_object_intercept_show_callback_add(o, _e_comp_object_util_show, z);
   evas_object_intercept_hide_callback_add(o, _e_comp_object_util_hide, z);
   evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE, _e_comp_object_util_moveresize, z);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _e_comp_object_util_del, z);
   evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, _e_comp_object_util_moveresize, z);
   evas_object_event_callback_add(o, EVAS_CALLBACK_RESTACK, _e_comp_object_util_restack, z);

   e_comp_object_signal_emit(o, "e,state,hidden", "e");

   edje_object_part_swallow(o, "e.swallow.content", z ?: obj);

   _e_comp_object_event_add(o);

   if (vis)
     evas_object_show(o);

   return o;
}

E_API Evas_Object *
e_comp_object_util_get(Evas_Object *obj)
{
   Evas_Object *z;
   SOFT_ENTRY(NULL);
   if (cw) return NULL;
   z = edje_object_part_swallow_get(obj, "e.swallow.content");
   EINA_SAFETY_ON_NULL_RETURN_VAL(z, NULL);
   return e_zoomap_child_get(z);
}

/* utility functions for deleting objects when their "owner" is deleted */
E_API void
e_comp_object_util_del_list_append(Evas_Object *obj, Evas_Object *to_del)
{
   Eina_List *l;

   EINA_SAFETY_ON_NULL_RETURN(to_del);
   l = evas_object_data_get(obj, "comp_object-to_del");
   evas_object_data_set(obj, "comp_object-to_del", eina_list_append(l, to_del));
   evas_object_event_callback_del(obj, EVAS_CALLBACK_DEL, _e_comp_object_util_del);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _e_comp_object_util_del, NULL);
}

E_API void
e_comp_object_util_del_list_remove(Evas_Object *obj, Evas_Object *to_del)
{
   Eina_List *l;

   SOFT_ENTRY();
   EINA_SAFETY_ON_NULL_RETURN(to_del);
   l = evas_object_data_get(obj, "comp_object-to_del");
   if (l)
     evas_object_data_set(obj, "comp_object-to_del", eina_list_remove(l, to_del));
}

/////////////////////////////////////////////////////////

E_API Evas_Object *
e_comp_object_client_add(E_Client *ec)
{
   Evas_Object *o;
   E_Comp_Object *cw;

   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
   if (ec->frame) return NULL;
   _e_comp_smart_init();
   o = evas_object_smart_add(e_comp->evas, _e_comp_smart);
   cw = evas_object_smart_data_get(o);
   if (!cw)
     {
        evas_object_del(o);
        return NULL;
     }
   evas_object_data_set(o, "E_Client", ec);
//   REFD(ec, 9);
//   e_object_ref(E_OBJECT(ec));
   cw->ec = ec;
   ec->frame = o;
   evas_object_data_set(o, "comp_object", (void*)1);
   e_object_delfn_add(E_OBJECT(ec), _e_comp_object_client_del, cw);

   _e_comp_object_event_add(o);

   return o;
}

/* utility functions for getting client inset */
E_API void
e_comp_object_frame_xy_adjust(Evas_Object *obj, int x, int y, int *ax, int *ay)
{
   API_ENTRY;
   if (!cw->client_inset.calc)
     {
        if (ax) *ax = x;
        if (ay) *ay = y;
        return;
     }
   if (ax) *ax = x - cw->client_inset.l;
   if (ay) *ay = y - cw->client_inset.t;
}

E_API void
e_comp_object_frame_xy_unadjust(Evas_Object *obj, int x, int y, int *ax, int *ay)
{
   API_ENTRY;
   if (!cw->client_inset.calc)
     {
        if (ax) *ax = x;
        if (ay) *ay = y;
        return;
     }
   if (ax) *ax = x + cw->client_inset.l;
   if (ay) *ay = y + cw->client_inset.t;
}

E_API void
e_comp_object_frame_wh_adjust(Evas_Object *obj, int w, int h, int *aw, int *ah)
{
   API_ENTRY;
   if (!cw->client_inset.calc)
     {
        if (aw) *aw = w;
        if (ah) *ah = h;
        return;
     }
   if (aw) *aw = w + cw->client_inset.l + cw->client_inset.r;
   if (ah) *ah = h + cw->client_inset.t + cw->client_inset.b;
}

E_API void
e_comp_object_frame_wh_unadjust(Evas_Object *obj, int w, int h, int *aw, int *ah)
{
   API_ENTRY;
   if (!cw->client_inset.calc)
     {
        if (aw) *aw = w;
        if (ah) *ah = h;
        return;
     }
   if (aw) *aw = w - cw->client_inset.l - cw->client_inset.r;
   if (ah) *ah = h - cw->client_inset.t - cw->client_inset.b;
}

E_API E_Client *
e_comp_object_client_get(Evas_Object *obj)
{
   Evas_Object *o;

   SOFT_ENTRY(NULL);
   /* FIXME: remove this when eo is used */
   o = evas_object_data_get(obj, "comp_smart_obj");
   if (o)
     return e_comp_object_client_get(o);
   return cw ? cw->ec : NULL;
}

E_API void
e_comp_object_frame_extends_get(Evas_Object *obj, int *x, int *y, int *w, int *h)
{
   API_ENTRY;
   if (cw->frame_extends)
     edje_object_parts_extends_calc(cw->frame_object, x, y, w, h);
   else
     {
        if (x) *x = 0;
        if (y) *y = 0;
        if (w) *w  = cw->ec->w;
        if (h) *h  = cw->ec->h;
     }
}

E_API E_Zone *
e_comp_object_util_zone_get(Evas_Object *obj)
{
   E_Zone *zone = NULL;

   SOFT_ENTRY(NULL);
   if (cw)
     zone = cw->ec->zone;
   if (!zone)
     {
        int x, y, w, h;

        if (e_win_client_get(obj))
          return e_win_client_get(obj)->zone;
        evas_object_geometry_get(obj, &x, &y, &w, &h);
        zone = e_comp_zone_xy_get(x, y);
        if (zone) return zone;
        zone = e_comp_zone_xy_get(x + w, y + h);
        if (zone) return zone;
        zone = e_comp_zone_xy_get(x + w, y);
        if (zone) return zone;
        zone = e_comp_zone_xy_get(x, y + h);
     }
   return zone;
}

E_API void
e_comp_object_util_center_on_zone(Evas_Object *obj, E_Zone *zone)
{
   int x, y, w, h, ow, oh;

   SOFT_ENTRY();

   EINA_SAFETY_ON_NULL_RETURN(zone);
   e_zone_useful_geometry_get(zone, &x, &y, &w, &h);
   if (cw)
     ow = cw->ec->w, oh = cw->ec->h;
   else
     evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
   x = x + (w - ow) / 2;
   y = y + (h - oh) / 2;
   evas_object_move(obj, x, y);
}

E_API void
e_comp_object_util_center(Evas_Object *obj)
{
   E_Zone *zone;

   zone = e_comp_object_util_zone_get(obj);

   e_comp_object_util_center_on_zone(obj, zone);
}

E_API void
e_comp_object_util_center_on(Evas_Object *obj, Evas_Object *on)
{
   int x, y, w, h, ow, oh;
   E_Comp_Object *cw2 = NULL;
   const char *type;

   SOFT_ENTRY();
   EINA_SAFETY_ON_NULL_RETURN(on);

   type = evas_object_type_get(on);
   if ((type) && (!strcmp(type, "e_comp_object")))
     cw2 = evas_object_smart_data_get(on);
   if (cw2 && eina_streq(evas_object_type_get(on), SMART_NAME))
     x = cw2->ec->x, y = cw2->ec->y, w = cw2->ec->w, h = cw2->ec->h;
   else
     evas_object_geometry_get(on, &x, &y, &w, &h);
   if (cw)
     ow = cw->ec->w, oh = cw->ec->h;
   else
     evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
   evas_object_move(obj, x + (w / 2) - (ow / 2), y + (h / 2) - (oh / 2));
}

E_API void
e_comp_object_util_fullscreen(Evas_Object *obj)
{
   SOFT_ENTRY();

   if (cw)
     e_client_fullscreen(cw->ec, E_FULLSCREEN_RESIZE);
   else
     {
        evas_object_move(obj, 0, 0);
        evas_object_resize(obj, e_comp->w, e_comp->h);
     }
}

E_API void
e_comp_object_util_center_pos_get(Evas_Object *obj, int *x, int *y)
{
   E_Zone *zone;
   int zx, zy, zw, zh;
   int ow, oh;
   SOFT_ENTRY();

   if (cw)
     ow = cw->w, oh = cw->h;
   else
     evas_object_geometry_get(obj, NULL, NULL, &ow, &oh);
   zone = e_comp_object_util_zone_get(obj);
   e_zone_useful_geometry_get(zone, &zx, &zy, &zw, &zh);
   if (x) *x = zx + (zw - ow) / 2;
   if (y) *y = zy + (zh - oh) / 2;
}

E_API void
e_comp_object_input_area_set(Evas_Object *obj, const Eina_Tiler *area)
{
   Eina_Rectangle *rect;
   Eina_Iterator *it;

   API_ENTRY;

   if ((!area) && (!cw->input_area)) return;
   if (area && cw->input_area && eina_tiler_equal(area, cw->input_area)) return;
   _e_comp_input_objs_free(cw);
   if (cw->obj) evas_object_pass_events_set(cw->obj, 1);
   if ((!area) || eina_tiler_empty(area))
     {
        E_FREE_FUNC(cw->input_area, eina_tiler_free);
        E_FREE_FUNC(cw->input_objs, eina_array_free);
        return;
     }
   if (cw->input_area)
     eina_tiler_clear(cw->input_area);
   else
     {
        cw->input_area = eina_tiler_new(99999, 99999);
        eina_tiler_tile_size_set(cw->input_area, 1, 1);
     }
   if (!cw->input_objs)
     cw->input_objs = eina_array_new(1);
   it = eina_tiler_iterator_new(area);
   EINA_ITERATOR_FOREACH(it, rect)
     {
        Evas_Object *o = evas_object_rectangle_add(e_comp->evas);
        //e_util_size_debug_set(o, 1);
        evas_object_name_set(o, "cw->input_rect");
        evas_object_color_set(o, 0, 0, 0, 0);
        evas_object_clip_set(o, cw->clip);
        evas_object_smart_member_add(o, obj);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_IN, _e_comp_object_ssd_mouse_in, cw);
        evas_object_event_callback_add(o, EVAS_CALLBACK_MOUSE_OUT, _e_comp_object_ssd_mouse_out, cw);
        if (cw->ec->moving)
          evas_object_pointer_mode_set(o, EVAS_OBJECT_POINTER_MODE_NOGRAB);
        evas_object_show(o);
        eina_array_push(cw->input_objs, o);
        eina_tiler_rect_add(cw->input_area, rect);
     }
   eina_iterator_free(it);
   _e_comp_object_input_rect_update(cw);
}

E_API Eina_Bool
e_comp_object_coords_inside_input_area(Evas_Object *obj, int x, int y)
{
   API_ENTRY EINA_FALSE;

   if (cw->input_area)
     {
        unsigned int i;
        Evas_Object *rect;
        Eina_Array_Iterator it;

        EINA_ARRAY_ITER_NEXT(cw->input_objs, i, rect, it)
          if (evas_object_pointer_coords_inside_get(rect, x, y)) return EINA_TRUE;

        return EINA_FALSE;
     }
   if (evas_object_pass_events_get(obj)) return EINA_FALSE;
   return E_INSIDE(x, y, cw->ec->x, cw->ec->y, cw->ec->w, cw->ec->h);
}

E_API void
e_comp_object_frame_geometry_get(Evas_Object *obj, int *l, int *r, int *t, int *b)
{
   API_ENTRY;
   if (l) *l = cw->client_inset.l;
   if (r) *r = cw->client_inset.r;
   if (t) *t = cw->client_inset.t;
   if (b) *b = cw->client_inset.b;
}

/* set geometry for CSD */
E_API void
e_comp_object_frame_geometry_set(Evas_Object *obj, int l, int r, int t, int b)
{
   Eina_Bool calc;

   API_ENTRY;
   if (cw->frame_object)
     CRI("ACK!");
   if ((cw->client_inset.l == l) && (cw->client_inset.r == r) &&
       (cw->client_inset.t == t) && (cw->client_inset.b == b)) return;
   calc = cw->client_inset.calc;
   cw->client_inset.calc = l || r || t || b;
   eina_stringshare_replace(&cw->frame_theme, "borderless");
   if (cw->client_inset.calc)
     {
        cw->ec->w += (l + r) - (cw->client_inset.l + cw->client_inset.r);
        cw->ec->h += (t + b) - (cw->client_inset.t + cw->client_inset.b);
        if (cw->ec->maximized || cw->ec->fullscreen)
          cw->ec->saved.frame = 0;
     }
   else if (cw->ec->maximized || cw->ec->fullscreen)
     {
        cw->ec->saved.x += l - cw->client_inset.l;
        cw->ec->saved.y += t - cw->client_inset.t;
        if (!e_client_has_xwindow(cw->ec))
          {
             cw->ec->saved.w -= ((l + r) - (cw->client_inset.l + cw->client_inset.r));
             cw->ec->saved.h -= ((t + b) - (cw->client_inset.t + cw->client_inset.b));
          }
     }
   if (!cw->ec->new_client)
     {
        if (calc && cw->client_inset.calc)
          {
             E_Zone *zone = e_comp_object_util_zone_get(obj);

             if (cw->ec->x != zone->x)
               cw->ec->x -= l - cw->client_inset.l;
             if (cw->ec->y != zone->y)
               cw->ec->y -= t - cw->client_inset.t;
          }
        cw->ec->changes.pos = cw->ec->changes.size = 1;
        EC_CHANGED(cw->ec);
     }
   cw->client_inset.l = l;
   cw->client_inset.r = r;
   cw->client_inset.t = t;
   cw->client_inset.b = b;
   if (!cw->shobj) return;
   if (cw->client_inset.calc)
     e_comp_object_signal_emit(obj, "e,state,focus,disabled", "e");
   else
     e_comp_object_signal_emit(obj, "e,state,focus,enabled", "e");
}

E_API Eina_Bool
e_comp_object_frame_allowed(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;
   return (!e_client_util_ignored_get(cw->ec)) && (!cw->ec->mwm.borderless) &&
     (cw->frame_object || (!cw->client_inset.calc));
}

E_API void
e_comp_object_frame_icon_geometry_get(Evas_Object *obj, int *x, int *y, int *w, int *h)
{
   API_ENTRY;

   if (x) *x = 0;
   if (y) *y = 0;
   if (w) *w = 0;
   if (h) *h = 0;
   if (!cw->frame_icon) return;
   evas_object_geometry_get(cw->frame_icon, x, y, w, h);
}

E_API Eina_Bool
e_comp_object_frame_title_set(Evas_Object *obj, const char *name)
{
   API_ENTRY EINA_FALSE;
   if (!e_util_strcmp(cw->frame_name, name)) return EINA_FALSE;
   eina_stringshare_replace(&cw->frame_name, name);
   if (cw->frame_object)
     edje_object_part_text_set(cw->frame_object, "e.text.title", cw->frame_name);
   return EINA_TRUE;
}

E_API Eina_Bool
e_comp_object_frame_exists(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;
   return !!cw->frame_object;
}

E_API Eina_Bool
e_comp_object_frame_icon_update(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;

   E_FREE_FUNC(cw->frame_icon, evas_object_del);
   if (!cw->frame_object) return EINA_FALSE;
   if (!edje_object_part_exists(cw->frame_object, "e.swallow.icon"))
     return EINA_TRUE;
   cw->frame_icon = e_client_icon_add(cw->ec, e_comp->evas);
   if (!cw->frame_icon) return EINA_TRUE;
   if (!edje_object_part_swallow(cw->frame_object, "e.swallow.icon", cw->frame_icon))
     E_FREE_FUNC(cw->frame_icon, evas_object_del);
   return EINA_TRUE;
}

static void
_e_comp_object_volume_update(Evas_Object *obj)
{
   API_ENTRY;

   if (cw->ec->focused)
     e_client_volume_object_emit(cw->ec, "e,state,focused", "e");
   else
     e_client_volume_object_emit(cw->ec, "e,state,unfocused", "e");
   if (cw->ec->urgent)
     e_client_volume_object_emit(cw->ec, "e,state,urgent", "e");
   else
     e_client_volume_object_emit(cw->ec, "e,state,not_urgent", "e");
   if (cw->ec->shaded)
     e_client_volume_object_emit(cw->ec, "e,state,shaded", "e");
   if (cw->ec->sticky)
     e_client_volume_object_emit(cw->ec, "e,state,sticky", "e");
   if (cw->ec->hung)
     e_client_volume_object_emit(cw->ec, "e,state,hung", "e");
   /* visibility must always be enabled for re_manage clients to prevent
    * pop-in animations every time the user sees a persistent client again;
    * applying visibility for iconic clients prevents the client from getting
    * stuck as hidden
    */
   if (cw->visible || cw->ec->iconic || cw->ec->re_manage)
     {
        if ((cw->w > 0) && (cw->h > 0))
          e_client_volume_object_emit(cw->ec, "e,state,visible", "e");
     }
   else
     e_client_volume_object_emit(cw->ec, "e,state,hidden", "e");
   if (e_comp_object_frame_allowed(cw->smart_obj))
     e_client_volume_object_emit(cw->ec, "e,state,focus,enabled", "e");
   else
     e_client_volume_object_emit(cw->ec, "e,state,focus,disabled", "e");
}

E_API void
e_comp_object_frame_volume_update(Evas_Object *obj)
{
   API_ENTRY;

   if (!cw->frame_object) return;
   if (edje_object_part_exists(cw->frame_object, "e.swallow.volume"))
     {
        if (cw->ec->sinks)
          {
             if (!cw->frame_volume)
               {
                  cw->frame_volume = e_client_volume_object_add(cw->ec, e_comp->evas);
                  edje_object_part_swallow(cw->frame_object,
                                           "e.swallow.volume", cw->frame_volume);
                  _e_comp_object_volume_update(obj);
                  evas_object_show(cw->frame_volume);
               }
          }
        else
          {
             E_FREE_FUNC(cw->frame_volume, evas_object_del);
          }
     }
}

E_API Eina_Bool
e_comp_object_frame_theme_set(Evas_Object *obj, const char *name)
{
   Evas_Object *o, *pbg;
   char buf[4096];
   int ok;
   Eina_Stringshare *theme;

   API_ENTRY EINA_FALSE;

   if (!e_util_strcmp(cw->frame_theme, name))
    return edje_object_part_swallow(cw->shobj, "e.swallow.content", cw->frame_object ?: cw->obj);
   if (!e_util_strcmp(name, "COMP_RESHADOW"))
     return _e_comp_object_shadow_setup(cw);
   if (eina_streq(name, "borderless") && (!cw->frame_object)) return EINA_TRUE;
   pbg = cw->frame_object;
   theme = eina_stringshare_add(name);

   if (cw->frame_object)
     {
        int w, h;

        w = cw->ec->w, h = cw->ec->h;
        e_comp_object_frame_wh_unadjust(obj, w, h, &cw->ec->w, &cw->ec->h);
        if ((cw->ec->w != w) || (cw->ec->h != h))
          {
             cw->ec->changes.size = 1;
             EC_CHANGED(cw->ec);
          }
        E_FREE_FUNC(cw->frame_object, evas_object_del);
        if (!name) goto reshadow;
     }
   o = edje_object_add(e_comp->evas);
   snprintf(buf, sizeof(buf), "e/widgets/border/%s/border", name);
   ok = e_theme_edje_object_set(o, "base/theme/border", buf);
   if ((!ok) && (!e_util_strcmp(name, "borderless")))
     {
        cw->frame_object = NULL;
        E_FREE_FUNC(cw->frame_icon, evas_object_del);
        E_FREE_FUNC(cw->frame_volume, evas_object_del);
        evas_object_del(o);
        eina_stringshare_del(cw->frame_theme);
        cw->frame_theme = theme;
        goto reshadow;
     }
   if (!ok)
     {
        if (theme != e_config->theme_default_border_style)
          {
             snprintf(buf, sizeof(buf), "e/widgets/border/%s/border", e_config->theme_default_border_style);
             ok = e_theme_edje_object_set(o, "base/theme/border", buf);
          }
        if (!ok)
          {
             ok = e_theme_edje_object_set(o, "base/theme/border",
                                          "e/widgets/border/default/border");
             if (ok && (theme == e_config->theme_default_border_style))
               {
                  /* Reset default border style to default */
                  eina_stringshare_replace(&e_config->theme_default_border_style, "default");
                  e_config_save_queue();
               }
          }
     }

   if (ok)
     {
        cw->frame_object = o;
        edje_object_signal_emit(o, "e,version,22", "e");
        eina_stringshare_del(cw->frame_theme);
        cw->frame_theme = theme;
        evas_object_name_set(o, "cw->frame_object");

        if (cw->frame_name)
          edje_object_part_text_set(o, "e.text.title", cw->frame_name);

        if (pbg)
          {
             if (cw->frame_icon)
               {
                  if (!edje_object_part_swallow(cw->frame_object,
                                                "e.swallow.icon",
                                                cw->frame_icon))
                    E_FREE_FUNC(cw->frame_icon, evas_object_del);
               }
             if (cw->frame_volume)
               {
                  if (!edje_object_part_swallow(cw->frame_object,
                                                "e.swallow.volume",
                                                cw->frame_volume))
                    E_FREE_FUNC(cw->frame_volume, evas_object_del);
               }
          }
        else
          {
             cw->ec->changes.icon = 1;
             EC_CHANGED(cw->ec);
          }

        if (cw->ec->volume_control_enabled)
          {
             e_comp_object_frame_volume_update(obj);
          }
     }
   else
     {
        CRI("USER IS USING A SHITTY THEME! ABORT!!!!");
        evas_object_del(o);
        E_FREE_FUNC(cw->frame_icon, evas_object_del);
        E_FREE_FUNC(cw->frame_volume, evas_object_del);
     }
reshadow:
   if (cw->shobj)
     _e_comp_object_shadow_setup(cw);
   do
     {
        _e_comp_smart_cb_frame_recalc(cw, cw->smart_obj, NULL);
        if ((cw->x == -1) && (cw->y == -1) && cw->ec->new_client &&
            (!cw->ec->placed) && (!cw->ec->re_manage))
          {
             cw->ec->x = MAX(cw->ec->zone->x, cw->ec->client.x - cw->client_inset.l);
             cw->ec->y = MAX(cw->ec->zone->y, cw->ec->client.y - cw->client_inset.t);
          }
        /* this guarantees that we won't get blocked by the NOP check in the interceptor */
        cw->y = cw->x = -99999;
        if (pbg)
          evas_object_move(obj, cw->ec->x, cw->ec->y);
        else if (cw->ec->placed || (!cw->ec->new_client))
          {
             /* if no previous frame:
              * - reapply client_inset
              * - clamp to zone
              */
             int x, y;

             if (cw->ec->changes.size)
               {
                  x = cw->ec->x;
                  y = cw->ec->y;
               }
             else
               {
                  x = cw->ec->client.x, y = cw->ec->client.y;
                  x = MAX(cw->ec->zone->x, cw->ec->client.x - cw->client_inset.l);
                  y = MAX(cw->ec->zone->y, cw->ec->client.y - cw->client_inset.t);
               }
             evas_object_move(obj, x, y);
          }
     } while (0);

   if (cw->ec->maximized)
     {
        cw->ec->changes.need_maximize = 1;
        EC_CHANGED(cw->ec);
     }
   evas_object_smart_callback_call(cw->smart_obj, "frame_changed", NULL);
   if (cw->frame_object)
     {
        cw->frame_extends = !!edje_object_data_get(cw->frame_object, "frame_extends");
        edje_object_signal_callback_add(cw->frame_object, "*", "*",
                                        _e_comp_object_cb_signal_bind, cw);
     }
   else
     cw->frame_extends = 0;
   if (pbg && (!cw->frame_object))
     {
        evas_object_event_callback_del(cw->obj, EVAS_CALLBACK_MOUSE_IN, _e_comp_object_ssd_mouse_in);
        evas_object_event_callback_del(cw->obj, EVAS_CALLBACK_MOUSE_OUT, _e_comp_object_ssd_mouse_out);
     }
   else if (cw->obj)
     {
        evas_object_event_callback_add(cw->obj, EVAS_CALLBACK_MOUSE_IN, _e_comp_object_ssd_mouse_in, cw);
        evas_object_event_callback_add(cw->obj, EVAS_CALLBACK_MOUSE_OUT, _e_comp_object_ssd_mouse_out, cw);
     }
   evas_object_del(pbg);
   return EINA_TRUE;
}

E_API void
e_comp_object_signal_emit(Evas_Object *obj, const char *sig, const char *src)
{
   E_Comp_Object_Mover *prov;

   API_ENTRY;
   //INF("EMIT %p: %s %s", cw->ec, sig, src);
   edje_object_signal_emit(cw->shobj, sig, src);
   e_client_volume_object_emit(cw->ec, sig, src);
   if (cw->frame_object) edje_object_signal_emit(cw->frame_object, sig, src);
   if (cw->frame_icon && e_icon_edje_get(cw->frame_icon))
     edje_object_signal_emit(e_icon_edje_get(cw->frame_icon), sig, src);
   /* start with highest priority callback first */
   EINA_INLIST_REVERSE_FOREACH(_e_comp_object_movers, prov)
     {
        if (!e_util_glob_match(sig, prov->sig)) continue;
        if (prov->func(prov->data, obj, sig)) break;
     }
}

E_API void
e_comp_object_signal_callback_add(Evas_Object *obj, const char *sig, const char *src, Edje_Signal_Cb cb, const void *data)
{
   /* FIXME: at some point I guess this should use eo to inherit
    * -EDJE_OBJ_SUB_ID_SIGNAL_CALLBACK_ADD
    * -EDJE_OBJ_SUB_ID_SIGNAL_CALLBACK_DEL
    */
   API_ENTRY;
   edje_object_signal_callback_add(cw->shobj, sig, src, cb, (void*)data);
}

E_API void
e_comp_object_signal_callback_del(Evas_Object *obj, const char *sig, const char *src, Edje_Signal_Cb cb)
{
   API_ENTRY;
   edje_object_signal_callback_del(cw->shobj, sig, src, cb);
}

E_API void
e_comp_object_signal_callback_del_full(Evas_Object *obj, const char *sig, const char *src, Edje_Signal_Cb cb, const void *data)
{
   API_ENTRY;
   edje_object_signal_callback_del_full(cw->shobj, sig, src, cb, (void*)data);
}

E_API void
e_comp_object_damage(Evas_Object *obj, int x, int y, int w, int h)
{
   int tw, th;
   Eina_Rectangle rect;
   API_ENTRY;

   if (cw->ec->input_only) return;
   if (cw->nocomp) return;
   _e_comp_object_updates_init(cw);
   EINA_SAFETY_ON_NULL_RETURN(cw->updates);
   rect.x = x, rect.y = y;
   rect.w = w, rect.h = h;
   evas_object_smart_callback_call(obj, "damage", &rect);
   if (e_comp->nocomp)
     {
        cw->nocomp_need_update = EINA_TRUE;
        return;
     }
   /* ignore overdraw */
   if (cw->updates_full)
     {
        RENDER_DEBUG("IGNORED %p: %d,%d %dx%d", cw->ec, x, y, w, h);
        if (!e_object_is_del(E_OBJECT(cw->ec)))
          e_comp_object_render_update_add(obj);
        return;
     }
   /* clip rect to client surface */
   RENDER_DEBUG("DAMAGE(%d,%d %dx%d) CLIP(%dx%d)", x, y, w, h, cw->ec->client.w, cw->ec->client.h);
   E_RECTS_CLIP_TO_RECT(x, y, w, h, 0, 0, cw->ec->client.w, cw->ec->client.h);
   /* if rect is the total size of the client after clip, clear the updates
    * since this is guaranteed to be the whole region anyway
    */
   eina_tiler_area_size_get(cw->updates, &tw, &th);
   if ((w > tw) || (h > th))
     {
        RENDER_DEBUG("DAMAGE RESIZE %p: %dx%d", cw->ec, cw->ec->client.w, cw->ec->client.h);
        eina_tiler_clear(cw->updates);
        eina_tiler_area_size_set(cw->updates, cw->ec->client.w, cw->ec->client.h);
        x = 0, y = 0;
        tw = cw->ec->client.w, th = cw->ec->client.h;
     }
   if ((!x) && (!y) && (w == tw) && (h == th))
     {
        eina_tiler_clear(cw->updates);
        RENDER_DEBUG("DAMAGE FULL: %p", cw->ec);
        cw->updates_full = 1;
        cw->update_count = 0;
     }
   cw->update_count++;
   if (cw->update_count > UPDATE_MAX)
     {
        /* this is going to get really dumb, so just update the whole thing */
        eina_tiler_clear(cw->updates);
        cw->update_count = cw->updates_full = 1;
        eina_tiler_rect_add(cw->updates, &(Eina_Rectangle){0, 0, tw, th});
        RENDER_DEBUG("DAMAGE MAX: %dx%d", tw, th);
     }
   else
     {
        eina_tiler_rect_add(cw->updates, &(Eina_Rectangle){x, y, w, h});
        RENDER_DEBUG("DAMAGE: %d,%d %dx%d", x, y, w, h);
     }
   cw->updates_exist = 1;
   if (!e_object_is_del(E_OBJECT(cw->ec)))
     e_comp_object_render_update_add(obj);
}

E_API Eina_Bool
e_comp_object_damage_exists(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;
   return cw->updates_exist || cw->updates_full;
}

E_API void
e_comp_object_render_update_add(Evas_Object *obj)
{
   API_ENTRY;

   if (cw->ec->input_only || (!cw->updates) || (!cw->redirected)) return;
   if (e_object_is_del(E_OBJECT(cw->ec)))
     CRI("CAN'T RENDER A DELETED CLIENT!");
   if (!e_pixmap_usable_get(cw->ec->pixmap)) return;
   //if (e_client_util_resizing_get(cw->ec) && (e_pixmap_type_get(cw->ec->pixmap) == E_PIXMAP_TYPE_WL))
     //INF("WL RENDER UPDATE");
   if (!cw->update)
     {
        cw->update = 1;
        e_comp->updates = eina_list_append(e_comp->updates, cw->ec);
     }
   e_comp_render_queue();
}

E_API void
e_comp_object_render_update_del(Evas_Object *obj)
{
   API_ENTRY;

   if (cw->ec->input_only || (!cw->updates)) return;
   if (!cw->update) return;
   cw->update = 0;
   /* this gets called during comp animating to clear the update flag */
   if (e_comp->grabbed) return;
   e_comp->updates = eina_list_remove(e_comp->updates, cw->ec);
}

E_API void
e_comp_object_shape_apply(Evas_Object *obj)
{
   Eina_List *l;
   Evas_Object *o;
   unsigned int i, *pix, *p;
   int w, h, px, py;

   API_ENTRY;
   if (!cw->ec) return; //NYI
   if (cw->ec->shaped)
     {
        if ((cw->ec->shape_rects_num >= 1) &&
            (!_e_comp_shaped_check(cw->ec->client.w, cw->ec->client.h, cw->ec->shape_rects, cw->ec->shape_rects_num)))
           return;
     }
   if (cw->native)
     {
        ERR("BUGGER: shape with native surface? cw=%p", cw);
        return;
     }
   evas_object_image_size_get(cw->obj, &w, &h);
   if ((w < 1) || (h < 1)) return;

   //INF("SHAPE RENDER %p", cw->ec);

   p = pix = evas_object_image_data_get(cw->obj, 1);
   if (!pix)
     {
        evas_object_image_data_set(cw->obj, pix);
        _e_comp_object_alpha_set(cw);
        return;
     }
   if ((cw->ec->shaped) && (cw->ec->shape_rects))
     {
        unsigned char *spix, *sp;

        spix = calloc(w * h, sizeof(unsigned char));
        RENDER_DEBUG("SHAPE [%p] rects %i", cw->ec, cw->ec->shape_rects_num);
        for (i = 0; i < cw->ec->shape_rects_num; i++)
          {
             int rx, ry, rw, rh;

             rx = cw->ec->shape_rects[i].x; ry = cw->ec->shape_rects[i].y;
             rw = cw->ec->shape_rects[i].w; rh = cw->ec->shape_rects[i].h;
             E_RECTS_CLIP_TO_RECT(rx, ry, rw, rh, 0, 0, w, h);
             sp = spix + (w * ry) + rx;
             for (py = 0; py < rh; py++)
               {
                  for (px = 0; px < rw; px++)
                    {
                       *sp = 0xff; sp++;
                    }
                  sp += w - rw;
               }
          }
        sp = spix;
        for (py = 0; py < h; py++)
          {
             for (px = 0; px < w; px++)
               {
                  unsigned int mask, imask;

                  mask = ((unsigned int)(*sp)) << 24;
                  imask = mask >> 8;
                  imask |= imask >> 8;
                  imask |= imask >> 8;
                  *p = mask | (*p & imask);
                  //if (*sp) *p = 0xff000000 | *p;
                  //else *p = 0x00000000;
                  sp++;
                  p++;
               }
          }
        free(spix);
     }
   else
      {
         for (py = 0; py < h; py++)
           {
              for (px = 0; px < w; px++)
                *p |= 0xff000000;
           }
      }
   evas_object_image_data_set(cw->obj, pix);
   _e_comp_object_alpha_set(cw);
   evas_object_image_data_update_add(cw->obj, 0, 0, w, h);
   EINA_LIST_FOREACH(cw->obj_mirror, l, o)
     {
        evas_object_image_data_set(o, pix);
        evas_object_image_data_update_add(o, 0, 0, w, h);
        evas_object_image_alpha_set(o, 1);
     }
// don't need to fix alpha chanel as blending
// should be totally off here regardless of
// alpha channel content
}

/* helper function to simplify toggling of redirection for display servers which support it */
E_API void
e_comp_object_redirected_set(Evas_Object *obj, Eina_Bool set)
{
   API_ENTRY;

   set = !!set;
   if (cw->redirected == set) return;
   cw->redirected = set;
   if (set)
     {
        if (cw->updates_exist)
          e_comp_object_render_update_add(obj);
        else
          e_comp_object_damage(obj, 0, 0, cw->w, cw->h);
        evas_object_smart_callback_call(obj, "redirected", NULL);
     }
   else
     {
        Eina_List *l;
        Evas_Object *o;

        if (cw->ec->pixmap)
          e_pixmap_clear(cw->ec->pixmap);
        if (cw->native)
          evas_object_image_native_surface_set(cw->obj, NULL);
        evas_object_image_size_set(cw->obj, 1, 1);
        evas_object_image_data_set(cw->obj, NULL);
        EINA_LIST_FOREACH(cw->obj_mirror, l, o)
          {
             evas_object_image_size_set(o, 1, 1);
             evas_object_image_data_set(o, NULL);
             if (cw->native)
               evas_object_image_native_surface_set(o, NULL);
          }
        cw->native = 0;
        e_comp_object_render_update_del(obj);
        evas_object_smart_callback_call(obj, "unredirected", NULL);
     }
}

E_API void
e_comp_object_native_surface_set(Evas_Object *obj, Eina_Bool set)
{
   Evas_Native_Surface ns;
   Eina_List *l;
   Evas_Object *o;

   API_ENTRY;
   EINA_SAFETY_ON_NULL_RETURN(cw->ec);
   if (cw->ec->input_only) return;
   set = !!set;

   if (set)
     {
        switch (e_comp->comp_type)
          {
           case E_PIXMAP_TYPE_X:
             /* native requires gl enabled, texture from pixmap enabled, and a non-shaped client */
             set = e_comp->gl &&
                   e_comp_config_get()->texture_from_pixmap &&
                   !cw->ec->shaped;
             break;
           case E_PIXMAP_TYPE_WL:
             set = !e_pixmap_is_pixels(cw->ec->pixmap);
             break;
           default:
             set = 0;
          }
        if (set)
          set = (!!cw->ns) || e_pixmap_native_surface_init(cw->ec->pixmap, &ns);
     }
   if ((!set) && (!cw->native)) return;

   cw->native = set;
   evas_object_image_native_surface_set(cw->obj, set && (!cw->blanked) ? (cw->ns ?: &ns) : NULL);
   _e_comp_object_alpha_set(cw);
   EINA_LIST_FOREACH(cw->obj_mirror, l, o)
     {
        evas_object_image_native_surface_set(o, set ? (cw->ns ?: &ns) : NULL);
        evas_object_image_alpha_set(o, !!cw->ns ? 1 : cw->ec->argb);
     }
}

E_API void
e_comp_object_native_surface_override(Evas_Object *obj, Evas_Native_Surface *ns)
{
   API_ENTRY;
   if (cw->ec->input_only) return;
   E_FREE(cw->ns);
   if (ns)
     cw->ns = (void *)eina_memdup((unsigned char *)ns, sizeof(Evas_Native_Surface), 0);
   _e_comp_object_alpha_set(cw);
   if (cw->native)
     e_comp_object_native_surface_set(obj, cw->native);
   e_comp_object_damage(obj, 0, 0, cw->w, cw->h);
}

E_API void
e_comp_object_blank(Evas_Object *obj, Eina_Bool set)
{
   API_ENTRY;

   set = !!set;

   if (cw->blanked == set) return;
   cw->blanked = set;
   _e_comp_object_alpha_set(cw);
   if (set)
     {
        evas_object_image_native_surface_set(cw->obj, NULL);
        evas_object_image_data_set(cw->obj, NULL);
        return;
     }
   if (cw->native)
     e_comp_object_native_surface_set(obj, 1);
   e_comp_object_damage(obj, 0, 0, cw->w, cw->h);
}

/* mark an object as dirty and setup damages */
E_API void
e_comp_object_dirty(Evas_Object *obj)
{
   Eina_Iterator *it;
   Eina_Rectangle *rect;
   Eina_List *ll;
   Evas_Object *o;
   int w, h;
   Eina_Bool dirty, visible, alpha;
   int bx, by, bxx, byy;

   API_ENTRY;
   /* only actually dirty if pixmap is available */
   dirty = e_pixmap_size_get(cw->ec->pixmap, &w, &h);
   visible = cw->visible;
   if (!dirty) w = h = 1;
   evas_object_image_pixels_dirty_set(cw->obj, cw->blanked ? 0 : dirty);
   if (!dirty)
     evas_object_image_data_set(cw->obj, e_pixmap_image_data_get(cw->ec->pixmap));
   evas_object_image_size_set(cw->obj, w, h);

   RENDER_DEBUG("SIZE [%p]: %dx%d", cw->ec, w, h);
   if (cw->pending_updates)
     eina_tiler_area_size_set(cw->pending_updates, w, h);

   alpha = evas_object_image_alpha_get(cw->obj);
   EINA_LIST_FOREACH(cw->obj_mirror, ll, o)
     {
        //evas_object_image_border_set(o, bx, by, bxx, byy);
        //evas_object_image_border_center_fill_set(o, EVAS_BORDER_FILL_SOLID);
        if (!dirty)
          evas_object_image_data_set(o, NULL);
        evas_object_image_size_set(o, w, h);
        evas_object_image_pixels_dirty_set(o, dirty);
        evas_object_image_alpha_set(o, alpha);
        visible |= evas_object_visible_get(o);
     }
   if (!dirty)
     {
        if (!e_object_is_del(E_OBJECT(cw->ec)))
          ERR("ERROR FETCHING PIXMAP FOR %p", cw->ec);
        return;
     }

   e_pixmap_image_opaque_get(cw->ec->pixmap, &bx, &by, &bxx, &byy);
   if (bxx && byy)
     {
        bxx = w - (bx + bxx), byy = h - (by + byy);
        // XXX: FIXME: - keep at least ONE border > 0 to allow cutouts to work
        // evas doesn't have a good fix for this right now.... also
        // we still BLEND the center bit anyway as we dont switch to non
        // blend ... so ugh. evas issue tho.
        if (byy < 1) byy = 1;
        evas_object_image_border_set(cw->obj, bx, bxx, by, byy);
     }
   else
     {
        bx = by = bxx = byy = 0;
        evas_object_image_border_set(cw->obj, bx, bxx, by, byy);
        if (cw->client_inset.calc && (!cw->frame_object)) //CSD
          {
             bx = -cw->client_inset.l + 4, by = -cw->client_inset.t + 4;
             bxx = -cw->client_inset.r, byy = -cw->client_inset.b;
          }
     }
   if ((cw->border.bx != bx) || (cw->border.by != by) ||
       (cw->border.bxx != bxx) || (cw->border.byy != byy) ||
       (cw->border.w != w) || (cw->border.h != h))
   {
      Edje_Message_Int_Set *msg;
      Edje_Message_Int msg2;
      Eina_Bool id = (bx || by || bxx || byy);

      cw->border.bx = bx;
      cw->border.by = by;
      cw->border.bxx = bxx;
      cw->border.byy = byy;
      cw->border.w = w;
      cw->border.h = h;
      msg = alloca(sizeof(Edje_Message_Int_Set) + (sizeof(int) * 3));
      msg->count = 4;
      msg->val[0] = bx;
      msg->val[1] = by;
      msg->val[2] = bxx;
      msg->val[3] = byy;
      edje_object_message_send(cw->shobj, EDJE_MESSAGE_INT_SET, 1, msg);
      msg2.val = id;
      edje_object_message_send(cw->shobj, EDJE_MESSAGE_INT, 0, &msg2);
   }

   e_comp_object_native_surface_set(obj, 1);
   it = eina_tiler_iterator_new(cw->updates);
   EINA_ITERATOR_FOREACH(it, rect)
     {
        RENDER_DEBUG("UPDATE ADD [%p]: %d %d %dx%d", cw->ec, rect->x, rect->y, rect->w, rect->h);
        evas_object_image_data_update_add(cw->obj, rect->x, rect->y, rect->w, rect->h);
        EINA_LIST_FOREACH(cw->obj_mirror, ll, o)
          evas_object_image_data_update_add(o, rect->x, rect->y, rect->w, rect->h);
        if (cw->pending_updates)
          eina_tiler_rect_add(cw->pending_updates, rect);
     }
   eina_iterator_free(it);
   if (cw->pending_updates)
     eina_tiler_clear(cw->updates);
   else
     {
        cw->pending_updates = cw->updates;
        cw->updates = eina_tiler_new(w, h);
        eina_tiler_tile_size_set(cw->updates, 1, 1);
     }
   cw->update_count = cw->updates_full = cw->updates_exist = 0;
   evas_object_smart_callback_call(obj, "dirty", NULL);
   if (cw->real_hid || cw->visible || (!visible) || (!cw->pending_updates) || cw->native) return;
   /* force render if main object is hidden but mirrors are visible */
   RENDER_DEBUG("FORCING RENDER %p", cw->ec);
   e_comp_object_render(obj);
}

E_API Eina_Bool
e_comp_object_render(Evas_Object *obj)
{
   Eina_Iterator *it = NULL;
   Eina_Rectangle *r;
   Eina_List *l;
   Evas_Object *o;
   int stride, pw, ph;
   unsigned int *pix, *srcpix;
   Eina_Bool ret = EINA_FALSE;

   API_ENTRY EINA_FALSE;

   EINA_SAFETY_ON_NULL_RETURN_VAL(cw->ec, EINA_FALSE);
   if (cw->ec->input_only) return EINA_TRUE;
   e_comp_object_render_update_del(obj);
   if (!e_pixmap_size_get(cw->ec->pixmap, &pw, &ph)) return EINA_FALSE;
   //if (e_pixmap_type_get(cw->ec->pixmap) == E_PIXMAP_TYPE_WL)
     //INF("WL RENDER!");

   if (!cw->pending_updates)
     {
        WRN("RENDER [%p]: NO RECTS!", cw->ec);
        evas_object_image_data_set(cw->obj, NULL);
        EINA_LIST_FOREACH(cw->obj_mirror, l, o)
          evas_object_image_data_set(o, NULL);
        return EINA_FALSE;
     }

   evas_object_image_pixels_dirty_set(cw->obj, EINA_FALSE);

   RENDER_DEBUG("RENDER SIZE: %dx%d", pw, ph);

   if (e_comp->comp_type == E_PIXMAP_TYPE_WL)
     {
        pix = e_pixmap_image_data_get(cw->ec->pixmap);
        ret = EINA_TRUE;
        goto end;
     }

   it = eina_tiler_iterator_new(cw->pending_updates);
   if (e_pixmap_image_is_argb(cw->ec->pixmap))
     {
        pix = e_pixmap_image_data_get(cw->ec->pixmap);
        EINA_ITERATOR_FOREACH(it, r)
          {
             E_RECTS_CLIP_TO_RECT(r->x, r->y, r->w, r->h, 0, 0, pw, ph);
             /* get pixmap data from rect region on display server into memory */
             ret = e_pixmap_image_draw(cw->ec->pixmap, r);
             if (!ret)
               {
                  WRN("UPDATE [%p]: %i %i %ix%i FAIL(%u)!!!!!!!!!!!!!!!!!", cw->ec, r->x, r->y, r->w, r->h, cw->failures);
                  if (++cw->failures < FAILURE_MAX)
                    e_comp_object_damage(obj, 0, 0, pw, ph);
                  else
                    {
                       DELD(cw->ec, 2);
                       e_object_del(E_OBJECT(cw->ec));
                       return EINA_FALSE;
                    }
                  break;
               }
             RENDER_DEBUG("UPDATE [%p] %i %i %ix%i", cw->ec, r->x, r->y, r->w, r->h);
          }
        if (!it) pix = NULL;
        goto end;
     }

   pix = evas_object_image_data_get(cw->obj, EINA_TRUE);
   stride = evas_object_image_stride_get(cw->obj);
   srcpix = e_pixmap_image_data_get(cw->ec->pixmap);
   EINA_ITERATOR_FOREACH(it, r)
     {
        E_RECTS_CLIP_TO_RECT(r->x, r->y, r->w, r->h, 0, 0, pw, ph);
        ret = e_pixmap_image_draw(cw->ec->pixmap, r);
        if (!ret)
          {
             WRN("UPDATE [%p]: %i %i %ix%i FAIL(%u)!!!!!!!!!!!!!!!!!", cw->ec, r->x, r->y, r->w, r->h, cw->failures);
             if (++cw->failures < FAILURE_MAX)
               e_comp_object_damage(obj, 0, 0, pw, ph);
             else
               {
                  DELD(cw->ec, 3);
                  e_object_del(E_OBJECT(cw->ec));
                  return EINA_FALSE;
               }
             break;
          }
        e_pixmap_image_data_argb_convert(cw->ec->pixmap, pix, srcpix, r, stride);
        RENDER_DEBUG("UPDATE [%p]: %d %d %dx%d -- pix = %p", cw->ec, r->x, r->y, r->w, r->h, pix);
     }
   if (!it) pix = NULL;
end:
   eina_iterator_free(it);
   evas_object_image_data_set(cw->obj, cw->blanked ? NULL : pix);
   _e_comp_object_alpha_set(cw);

   E_FREE_FUNC(cw->pending_updates, eina_tiler_free);
   if (ret)
     e_comp_client_post_update_add(cw->ec);
   return ret;
}

E_API Evas_Object *
e_comp_object_agent_add(Evas_Object *obj)
{
   Evas_Object *o;

   API_ENTRY NULL;

   o = evas_object_rectangle_add(e_comp->evas);
   evas_object_color_set(o, 0, 0, 0, 0);
   evas_object_pass_events_set(o, 1);
   evas_object_geometry_set(o, cw->ec->x, cw->ec->y, cw->ec->w, cw->ec->h);

   cw->obj_agent = eina_list_append(cw->obj_agent, o);
   evas_object_event_callback_add(o, EVAS_CALLBACK_RESIZE, _e_comp_object_cb_agent_resize, cw);
   evas_object_event_callback_add(o, EVAS_CALLBACK_MOVE, _e_comp_object_cb_agent_move, cw);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _e_comp_object_cb_agent_del, cw);
   return o;
}

/* create a duplicate of an evas object */
E_API Evas_Object *
e_comp_object_util_mirror_add(Evas_Object *obj)
{
   Evas_Object *o;
   int w, h;
   unsigned int *pix = NULL;

   SOFT_ENTRY(NULL);

   if (!cw)
     cw = evas_object_data_get(obj, "comp_mirror");
   if (!cw)
     {
        o = evas_object_image_filled_add(evas_object_evas_get(obj));
        evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
        evas_object_image_smooth_scale_set(o, e_comp_config_get()->smooth_windows);
        evas_object_image_alpha_set(o, 1);
        evas_object_image_source_set(o, obj);
        return o;
     }
   if ((!cw->ec) || (!e_pixmap_size_get(cw->ec->pixmap, &w, &h))) return NULL;
   o = evas_object_image_filled_add(evas_object_evas_get(obj));
   evas_object_image_colorspace_set(o, EVAS_COLORSPACE_ARGB8888);
   evas_object_image_smooth_scale_set(o, e_comp_config_get()->smooth_windows);
   cw->obj_mirror = eina_list_append(cw->obj_mirror, o);
   evas_object_event_callback_add(o, EVAS_CALLBACK_DEL, _e_comp_object_cb_mirror_del, cw);
   evas_object_event_callback_add(o, EVAS_CALLBACK_SHOW, _e_comp_object_cb_mirror_show, cw);
   evas_object_event_callback_add(o, EVAS_CALLBACK_HIDE, _e_comp_object_cb_mirror_hide, cw);
   evas_object_data_set(o, "E_Client", cw->ec);
   evas_object_data_set(o, "comp_mirror", cw);

   evas_object_image_size_set(o, w, h);
   evas_object_image_alpha_set(o, evas_object_image_alpha_get(cw->obj));

   if (cw->ec->shaped)
     {
        if (e_pixmap_image_data_get(cw->ec->pixmap))
          pix = evas_object_image_data_get(cw->obj, 0);
     }
   else
     {
        if (cw->native)
          {
             if (cw->ns)
               evas_object_image_native_surface_set(o, cw->ns);
             else
               {
                  Evas_Native_Surface ns;

                  e_pixmap_native_surface_init(cw->ec->pixmap, &ns);
                  evas_object_image_native_surface_set(o, &ns);
               }
          }
        else
          pix = e_pixmap_image_data_get(cw->ec->pixmap);
     }
   if (pix)
     {
      Eina_Bool dirty;
      //int bx, by, bxx, byy;

      dirty = evas_object_image_pixels_dirty_get(cw->obj);
      evas_object_image_pixels_dirty_set(o, dirty);
      //e_pixmap_image_opaque_get(cw->ec->pixmap, &bx, &by, &bxx, &byy);
      //if (bxx && byy)
        //bxx = cw->ec->client.w - (bx + bxx), byy = cw->ec->client.h - (by + byy);
      //else
        //bx = by = bxx = byy = 0;
      //evas_object_image_border_set(o, bx, by, bxx, byy);
      //evas_object_image_border_center_fill_set(o, EVAS_BORDER_FILL_SOLID);
      evas_object_image_data_set(o, pix);
      if (cw->ec->shaped)
        evas_object_image_data_set(cw->obj, pix);
      if (dirty)
        evas_object_image_data_update_add(o, 0, 0, w, h);
   }
   evas_object_image_pixels_get_callback_set(o, _e_comp_object_mirror_pixels_get, cw);
   return o;
}

//////////////////////////////////////////////////////

E_API Eina_Bool
e_comp_object_effect_allowed_get(Evas_Object *obj)
{
   API_ENTRY EINA_FALSE;

   if (!cw->shobj) return EINA_FALSE;
   return EINA_TRUE;
}

/* setup an api effect for a client */
E_API Eina_Bool
e_comp_object_effect_set(Evas_Object *obj, const char *effect)
{
   char buf[4096];
   Eina_Stringshare *grp;

   API_ENTRY EINA_FALSE;
   if (!cw->shobj) return EINA_FALSE; //input window

   if (!effect) effect = "none";
   snprintf(buf, sizeof(buf), "e/comp/effects/%s", effect);
   edje_object_file_get(cw->effect_obj, NULL, &grp);
   cw->effect_set = !eina_streq(effect, "none");
   if (!e_util_strcmp(buf, grp)) return cw->effect_set;
   if (!e_theme_edje_object_set(cw->effect_obj, "base/theme/comp", buf))
     {
        snprintf(buf, sizeof(buf), "e/comp/effects/auto/%s", effect);
        if (!e_theme_edje_object_set(cw->effect_obj, "base/theme/comp", buf))
          if (!e_theme_edje_object_set(cw->effect_obj, "base/theme/comp", "e/comp/effects/none"))
            {
               if (cw->effect_running)
                 {
                    if (!e_comp_object_effect_stop(obj, evas_object_data_get(cw->effect_obj, "_e_comp.end_cb")))
                      return EINA_FALSE;
                 }
               cw->effect_set = EINA_FALSE;
               return cw->effect_set;
            }
     }
   if (cw->effect_running)
     {
        if (!e_comp_object_effect_stop(obj, evas_object_data_get(cw->effect_obj, "_e_comp.end_cb")))
          return EINA_FALSE;
     }
   edje_object_part_swallow(cw->effect_obj, "e.swallow.content", cw->shobj);
   if (cw->effect_clip)
     {
        evas_object_clip_unset(cw->clip);
        cw->effect_clip = 0;
     }
   cw->effect_clip_able = !edje_object_data_get(cw->effect_obj, "noclip");
   return cw->effect_set;
}

/* set params for embryo scripts in effect */
E_API void
e_comp_object_effect_params_set(Evas_Object *obj, int id, int *params, unsigned int count)
{
   Edje_Message_Int_Set *msg;
   unsigned int x;

   API_ENTRY;
   EINA_SAFETY_ON_NULL_RETURN(params);
   EINA_SAFETY_ON_FALSE_RETURN(count);
   if (!cw->effect_set) return;

   msg = alloca(sizeof(Edje_Message_Int_Set) + ((count - 1) * sizeof(int)));
   msg->count = (int)count;
   for (x = 0; x < count; x++)
      msg->val[x] = params[x];
   edje_object_message_send(cw->effect_obj, EDJE_MESSAGE_INT_SET, id, msg);
   edje_object_message_signal_process(cw->effect_obj);
}

static void
_e_comp_object_effect_end_cb(void *data, Evas_Object *obj, const char *emission, const char *source)
{
   Edje_Signal_Cb end_cb;
   void *end_data;
   E_Comp_Object *cw = data;

   edje_object_signal_callback_del_full(obj, "e,action,done", "e", _e_comp_object_effect_end_cb, NULL);
   cw->effect_running = 0;
   if (!_e_comp_object_animating_end(cw)) return;
   e_comp_shape_queue();
   end_cb = evas_object_data_get(obj, "_e_comp.end_cb");
   if (!end_cb) return;
   end_data = evas_object_data_get(obj, "_e_comp.end_data");
   end_cb(end_data, cw->smart_obj, emission, source);

}

/* clip effect to client's zone */
E_API void
e_comp_object_effect_clip(Evas_Object *obj)
{
   API_ENTRY;
   if (!cw->ec->zone) return;
   if (cw->effect_clip) e_comp_object_effect_unclip(cw->smart_obj);
   if (!cw->effect_clip_able) return;
   evas_object_clip_set(cw->smart_obj, cw->ec->zone->bg_clip_object);
   cw->effect_clip = 1;
}

/* unclip effect from client's zone */
E_API void
e_comp_object_effect_unclip(Evas_Object *obj)
{
   API_ENTRY;
   if (!cw->effect_clip) return;
   evas_object_clip_unset(cw->smart_obj);
   cw->effect_clip = 0;
}

/* start effect, running end_cb after */
E_API Eina_Bool
e_comp_object_effect_start(Evas_Object *obj, Edje_Signal_Cb end_cb, const void *end_data)
{
   API_ENTRY EINA_FALSE;
   EINA_SAFETY_ON_NULL_RETURN_VAL(cw->ec, EINA_FALSE); //NYI
   if (!cw->effect_set) return EINA_FALSE;
   e_comp_object_effect_clip(obj);
   edje_object_signal_callback_del(cw->effect_obj, "e,action,done", "e", _e_comp_object_effect_end_cb);

   edje_object_signal_callback_add(cw->effect_obj, "e,action,done", "e", _e_comp_object_effect_end_cb, cw);
   evas_object_data_set(cw->effect_obj, "_e_comp.end_cb", end_cb);
   evas_object_data_set(cw->effect_obj, "_e_comp.end_data", end_data);

   edje_object_signal_emit(cw->effect_obj, "e,action,go", "e");
   _e_comp_object_animating_begin(cw);
   cw->effect_running = 1;
   return EINA_TRUE;
}

/* stop a currently-running effect immediately */
E_API Eina_Bool
e_comp_object_effect_stop(Evas_Object *obj, Edje_Signal_Cb end_cb)
{
   API_ENTRY EINA_FALSE;
   if (evas_object_data_get(cw->effect_obj, "_e_comp.end_cb") != end_cb) return EINA_TRUE;
   e_comp_object_effect_unclip(obj);
   if (cw->effect_clip)
     {
        evas_object_clip_unset(cw->effect_obj);
        cw->effect_clip = 0;
     }
   edje_object_signal_emit(cw->effect_obj, "e,action,stop", "e");
   edje_object_signal_callback_del_full(cw->effect_obj, "e,action,done", "e", _e_comp_object_effect_end_cb, cw);
   cw->effect_running = 0;
   return _e_comp_object_animating_end(cw);
}

static int
_e_comp_object_effect_mover_sort_cb(E_Comp_Object_Mover *a, E_Comp_Object_Mover *b)
{
   return a->pri - b->pri;
}

/* add a function to trigger based on signal emissions for the purpose of modifying effects */
E_API E_Comp_Object_Mover *
e_comp_object_effect_mover_add(int pri, const char *sig, E_Comp_Object_Mover_Cb provider, const void *data)
{
   E_Comp_Object_Mover *prov;

   prov = E_NEW(E_Comp_Object_Mover, 1);
   EINA_SAFETY_ON_NULL_RETURN_VAL(prov, NULL);
   prov->func = provider;
   prov->data = (void*)data;
   prov->pri = pri;
   prov->sig = sig;
   _e_comp_object_movers = eina_inlist_sorted_insert(_e_comp_object_movers, EINA_INLIST_GET(prov),
     (Eina_Compare_Cb)_e_comp_object_effect_mover_sort_cb);
   return prov;
}

E_API void
e_comp_object_effect_mover_del(E_Comp_Object_Mover *prov)
{
   EINA_SAFETY_ON_NULL_RETURN(prov);
   _e_comp_object_movers = eina_inlist_remove(_e_comp_object_movers, EINA_INLIST_GET(prov));
   free(prov);
}
////////////////////////////////////

static void
_e_comp_object_autoclose_cleanup(Eina_Bool already_del)
{
   if (e_comp->autoclose.obj)
     {
        e_comp_ungrab_input(0, 1);
        if (e_comp->autoclose.del_cb)
          e_comp->autoclose.del_cb(e_comp->autoclose.data, e_comp->autoclose.obj);
        else if (!already_del)
          {
             evas_object_hide(e_comp->autoclose.obj);
             E_FREE_FUNC(e_comp->autoclose.obj, evas_object_del);
          }
        E_FREE_FUNC(e_comp->autoclose.rect, evas_object_del);
     }
   e_comp->autoclose.obj = NULL;
   e_comp->autoclose.data = NULL;
   e_comp->autoclose.del_cb = NULL;
   e_comp->autoclose.key_cb = NULL;
   E_FREE_FUNC(e_comp->autoclose.key_handler, ecore_event_handler_del);
   e_comp_shape_queue();
}

static Eina_Bool
_e_comp_object_autoclose_key_down_cb(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   Ecore_Event_Key *ev = event;
   Eina_Bool del = EINA_TRUE;

   /* returning false in key_cb means delete the object */
   if (e_comp->autoclose.key_cb)
     del = !e_comp->autoclose.key_cb(e_comp->autoclose.data, ev);
   if (del) _e_comp_object_autoclose_cleanup(0);
   return ECORE_CALLBACK_DONE;
}

static void
_e_comp_object_autoclose_mouse_up_cb(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   _e_comp_object_autoclose_cleanup(0);
}

static void
_e_comp_object_autoclose_setup(Evas_Object *obj)
{
   if (!e_comp->autoclose.rect)
     {
        /* create rect just below autoclose object to catch mouse events */
        e_comp->autoclose.rect = evas_object_rectangle_add(e_comp->evas);
        evas_object_move(e_comp->autoclose.rect, 0, 0);
        evas_object_resize(e_comp->autoclose.rect, e_comp->w, e_comp->h);
        evas_object_show(e_comp->autoclose.rect);
        evas_object_name_set(e_comp->autoclose.rect, "e_comp->autoclose.rect");
        evas_object_color_set(e_comp->autoclose.rect, 0, 0, 0, 0);
        evas_object_event_callback_add(e_comp->autoclose.rect, EVAS_CALLBACK_MOUSE_UP, _e_comp_object_autoclose_mouse_up_cb, e_comp);
        e_comp_grab_input(0, 1);
     }
   evas_object_layer_set(e_comp->autoclose.rect, evas_object_layer_get(obj) - 1);
   evas_object_focus_set(obj, 1);
   e_comp_shape_queue();
   if (!e_comp->autoclose.key_handler)
     e_comp->autoclose.key_handler = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _e_comp_object_autoclose_key_down_cb, e_comp);
}

static void
_e_comp_object_autoclose_show(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   _e_comp_object_autoclose_setup(obj);
   evas_object_event_callback_del(obj, EVAS_CALLBACK_SHOW, _e_comp_object_autoclose_show);
}

static void
_e_comp_object_autoclose_del(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj, void *event_info EINA_UNUSED)
{
   evas_object_event_callback_del(obj, EVAS_CALLBACK_SHOW, _e_comp_object_autoclose_show);
   _e_comp_object_autoclose_cleanup(1);
   if (e_client_focused_get()) return;
   if (e_config->focus_policy != E_FOCUS_MOUSE)
     e_client_refocus();
}


E_API Eina_Bool
e_comp_object_util_autoclose_on_escape(void *d EINA_UNUSED, Ecore_Event_Key *ev)
{
   return !!strcmp(ev->key, "Escape");
}

E_API void
e_comp_object_util_autoclose(Evas_Object *obj, E_Comp_Object_Autoclose_Cb del_cb, E_Comp_Object_Key_Cb cb, const void *data)
{
   if (e_comp->autoclose.obj)
     {
        if (e_comp->autoclose.obj == obj) return;
        evas_object_event_callback_del_full(e_comp->autoclose.obj, EVAS_CALLBACK_DEL, _e_comp_object_autoclose_del, e_comp);
        _e_comp_object_autoclose_cleanup(0);
     }
   if (!obj) return;
   e_comp->autoclose.obj = obj;
   e_comp->autoclose.del_cb = del_cb;
   e_comp->autoclose.key_cb = cb;
   e_comp->autoclose.data = (void*)data;
   if (evas_object_visible_get(obj))
     _e_comp_object_autoclose_setup(obj);
   else
     evas_object_event_callback_add(obj, EVAS_CALLBACK_SHOW, _e_comp_object_autoclose_show, e_comp);
   evas_object_event_callback_add(obj, EVAS_CALLBACK_DEL, _e_comp_object_autoclose_del, e_comp);
}

E_API Evas_Object *
e_comp_object_frame_icon_get(Evas_Object *obj)
{
   API_ENTRY NULL;
   return cw->frame_icon;
}

E_API Evas_Object *
e_comp_object_frame_volume_get(Evas_Object *obj)
{
   API_ENTRY NULL;
   return cw->frame_volume;
}
