#include "e.h"

static int _e_client_hooks_delete = 0;
static int _e_client_hooks_walking = 0;

E_API int E_EVENT_CLIENT_ADD = -1;
E_API int E_EVENT_CLIENT_REMOVE = -1;
E_API int E_EVENT_CLIENT_ZONE_SET = -1;
E_API int E_EVENT_CLIENT_DESK_SET = -1;
E_API int E_EVENT_CLIENT_RESIZE = -1;
E_API int E_EVENT_CLIENT_MOVE = -1;
E_API int E_EVENT_CLIENT_SHOW = -1;
E_API int E_EVENT_CLIENT_HIDE = -1;
E_API int E_EVENT_CLIENT_ICONIFY = -1;
E_API int E_EVENT_CLIENT_UNICONIFY = -1;
E_API int E_EVENT_CLIENT_STACK = -1;
E_API int E_EVENT_CLIENT_FOCUS_IN = -1;
E_API int E_EVENT_CLIENT_FOCUS_OUT = -1;
E_API int E_EVENT_CLIENT_PROPERTY = -1;
E_API int E_EVENT_CLIENT_FULLSCREEN = -1;
E_API int E_EVENT_CLIENT_UNFULLSCREEN = -1;

static Eina_Hash *clients_hash[2] = {NULL}; // pixmap->client

static unsigned int focus_track_frozen = 0;

static int warp_to = 0;
static int warp_to_x = 0;
static int warp_to_y = 0;
static int warp_x[2] = {0}; //{cur,prev}
static int warp_y[2] = {0}; //{cur,prev}
static Ecore_Timer *warp_timer = NULL;

static E_Client *focused = NULL;
static E_Client *warp_client = NULL;
static E_Client *ecmove = NULL;
static E_Client *ecresize = NULL;
static E_Client *action_client = NULL;
static E_Drag *client_drag = NULL;

static Eina_List *focus_stack = NULL;
static Eina_List *raise_stack = NULL;

static Eina_Bool comp_grabbed = EINA_FALSE;
static Evas_Object *action_rect;

static Eina_List *handlers = NULL;
//static Eina_Bool client_grabbed = EINA_FALSE;

static Ecore_Event_Handler *action_handler_key = NULL;
static Ecore_Event_Handler *action_handler_mouse = NULL;
static Ecore_Timer *action_timer = NULL;
static Eina_Rectangle action_orig = {0, 0, 0, 0};

static E_Client_Layout_Cb _e_client_layout_cb = NULL;

EINTERN void e_client_focused_set(E_Client *ec);

static Eina_Inlist *_e_client_hooks[E_CLIENT_HOOK_LAST] = {NULL};

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

static Eina_Bool
_e_client_cb_efreet_cache_update(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED)
{
   const Eina_List *l;
   E_Client *ec;

   /* mark all clients for desktop/icon updates */
   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        E_FREE_FUNC(ec->desktop, efreet_desktop_free);
        if (e_object_is_del(E_OBJECT(ec))) continue;
        ec->changes.icon = 1;
        EC_CHANGED(ec);
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_client_cb_config_icon_theme(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED)
{
   const Eina_List *l;
   E_Client *ec;

   /* mark all clients for desktop/icon updates */
   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        if (e_object_is_del(E_OBJECT(ec))) continue;
        ec->changes.icon = 1;
        EC_CHANGED(ec);
     }
   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool
_e_client_cb_config_mode(void *data EINA_UNUSED, int type EINA_UNUSED, void *ev EINA_UNUSED)
{
   const Eina_List *l;
   E_Client *ec;
   E_Layer layer;

   /* move fullscreen borders above everything */

   if (e_config->mode.presentation)
     layer = E_LAYER_CLIENT_TOP;
   else if (!e_config->allow_above_fullscreen)
     layer = E_LAYER_CLIENT_FULLSCREEN;
   else
     return ECORE_CALLBACK_RENEW;

   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        if (e_object_is_del(E_OBJECT(ec))) continue;
        if ((ec->fullscreen) || (ec->need_fullscreen))
          {
             ec->fullscreen = 0;
             evas_object_layer_set(ec->frame, layer);
             ec->fullscreen = 1;
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_e_client_cb_pointer_warp(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Pointer_Warp *ev)
{
   if (ecmove)
     evas_object_move(ecmove->frame, ecmove->x + (ev->curr.x - ev->prev.x), ecmove->y + (ev->curr.y - ev->prev.y));
   return ECORE_CALLBACK_RENEW;
}


static Eina_Bool
_e_client_cb_desk_window_profile_change(void *data EINA_UNUSED, int type EINA_UNUSED, E_Event_Desk_Window_Profile_Change *ev EINA_UNUSED)
{
   const Eina_List *l;
   E_Client *ec;

   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        if (e_object_is_del(E_OBJECT(ec))) continue;
        ec->e.fetch.profile = 1;
        EC_CHANGED(ec);
     }
   return ECORE_CALLBACK_RENEW;
}

static void
_e_client_cb_drag_finished(E_Drag *drag, int dropped EINA_UNUSED)
{
   E_Client *ec;

   ec = drag->data;
   UNREFD(ec, 1);
   e_object_unref(E_OBJECT(ec));
   client_drag = NULL;
}

static void
_e_client_desk_window_profile_wait_desk_delfn(void *data, void *obj)
{
   E_Client *ec = data;
   E_Desk *desk = obj, *new_desk;
   const char *p;
   int i;

   if (e_object_is_del(E_OBJECT(ec))) return;

   ec->e.state.profile.wait_desk_delfn = NULL;
   eina_stringshare_replace(&ec->e.state.profile.wait, NULL);
   if (ec->e.state.profile.wait_desk)
     e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk));
   ec->e.state.profile.wait_desk = NULL;
   ec->e.state.profile.wait_for_done = 0;

   if (!ec->e.state.profile.use) return;

   new_desk = e_comp_desk_window_profile_get(desk->window_profile);
   if (new_desk)
     e_client_desk_set(ec, new_desk);
   else
     {
        for (i = 0; i < ec->e.state.profile.num; i++)
          {
             p = ec->e.state.profile.available_list[i];
             new_desk = e_comp_desk_window_profile_get(p);
             if (new_desk)
               {
                  e_client_desk_set(ec, new_desk);
                  break;
               }
          }
     }
}
////////////////////////////////////////////////


static Eina_Bool
_e_client_pointer_warp_to_center_timer(void *data EINA_UNUSED)
{
   if (warp_to && warp_client)
     {
        int x, y;
        double spd;

        ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
        /* move hasn't happened yet */
        if ((x == warp_x[1]) && (y == warp_y[1]))
           return EINA_TRUE;
        if ((abs(x - warp_x[0]) > 5) || (abs(y - warp_y[0]) > 5))
          {
             /* User moved the mouse, so stop warping */
             warp_to = 0;
             goto cleanup;
          }

        spd = e_config->pointer_warp_speed;
        warp_x[1] = x = warp_x[0];
        warp_y[1] = y = warp_y[0];
        warp_x[0] = (x * (1.0 - spd)) + (warp_to_x * spd);
        warp_y[0] = (y * (1.0 - spd)) + (warp_to_y * spd);
        if ((warp_x[0] == x) && (warp_y[0] == y))
          {
             warp_x[0] = warp_to_x;
             warp_y[0] = warp_to_y;
             warp_to = 0;
             goto cleanup;
          }
        ecore_evas_pointer_warp(e_comp->ee, warp_x[0], warp_y[0]);
        return ECORE_CALLBACK_RENEW;
     }
cleanup:
   E_FREE_FUNC(warp_timer, ecore_timer_del);
   if (warp_client)
     {
        warp_x[0] = warp_x[1] = warp_y[0] = warp_y[1] = -1;
        if (warp_client->modal)
          {
             warp_client = NULL;
             return ECORE_CALLBACK_CANCEL;
          }
        e_focus_event_mouse_in(warp_client);
        if (warp_client->iconic)
          {
             if (!warp_client->lock_user_iconify)
               e_client_uniconify(warp_client);
          }
        if (warp_client->shaded)
          {
             if (!warp_client->lock_user_shade)
               e_client_unshade(warp_client, warp_client->shade_dir);
          }

        if (!warp_client->lock_focus_out)
          {
             evas_object_focus_set(warp_client->frame, 1);
             e_client_focus_latest_set(warp_client);
          }
        warp_client = NULL;
     }
   return ECORE_CALLBACK_CANCEL;
}

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

static void
_e_client_hooks_clean(void)
{
   Eina_Inlist *l;
   E_Client_Hook *ch;
   unsigned int x;

   for (x = 0; x < E_CLIENT_HOOK_LAST; x++)
     EINA_INLIST_FOREACH_SAFE(_e_client_hooks[x], l, ch)
       {
          if (!ch->delete_me) continue;
          _e_client_hooks[x] = eina_inlist_remove(_e_client_hooks[x], EINA_INLIST_GET(ch));
          free(ch);
       }
}

static Eina_Bool
_e_client_hook_call(E_Client_Hook_Point hookpoint, E_Client *ec)
{
   E_Client_Hook *ch;

   e_object_ref(E_OBJECT(ec));
   _e_client_hooks_walking++;
   EINA_INLIST_FOREACH(_e_client_hooks[hookpoint], ch)
     {
        if (ch->delete_me) continue;
        ch->func(ch->data, ec);
        if ((hookpoint != E_CLIENT_HOOK_DEL) &&
          (hookpoint != E_CLIENT_HOOK_MOVE_END) &&
          (hookpoint != E_CLIENT_HOOK_RESIZE_END) &&
          (hookpoint != E_CLIENT_HOOK_FOCUS_UNSET) &&
          e_object_is_del(E_OBJECT(ec)))
          break;
     }
   _e_client_hooks_walking--;
   if ((_e_client_hooks_walking == 0) && (_e_client_hooks_delete > 0))
     _e_client_hooks_clean();
   return !!e_object_unref(E_OBJECT(ec));
}

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

static void
_e_client_event_simple_free(void *d EINA_UNUSED, E_Event_Client *ev)
{
   UNREFD(ev->ec, 3);
   e_object_unref(E_OBJECT(ev->ec));
   free(ev);
}

static void
_e_client_event_simple(E_Client *ec, int type)
{
   E_Event_Client *ev;

   ev = E_NEW(E_Event_Client, 1);
   ev->ec = ec;
   REFD(ec, 3);
   e_object_ref(E_OBJECT(ec));
   ecore_event_add(type, ev, (Ecore_End_Cb)_e_client_event_simple_free, NULL);
}

static void
_e_client_event_property(E_Client *ec, int prop)
{
   E_Event_Client_Property *ev;

   ev = E_NEW(E_Event_Client_Property, 1);
   ev->ec = ec;
   ev->property = prop;
   REFD(ec, 33);
   e_object_ref(E_OBJECT(ec));
   ecore_event_add(E_EVENT_CLIENT_PROPERTY, ev, (Ecore_End_Cb)_e_client_event_simple_free, NULL);
}

static void
_e_client_event_desk_set_free(void *d EINA_UNUSED, E_Event_Client_Desk_Set *ev)
{
   UNREFD(ev->ec, 4);
   e_object_unref(E_OBJECT(ev->ec));
   e_object_unref(E_OBJECT(ev->desk));
   free(ev);
}

static void
_e_client_event_zone_set_free(void *d EINA_UNUSED, E_Event_Client_Zone_Set *ev)
{
   UNREFD(ev->ec, 5);
   e_object_unref(E_OBJECT(ev->ec));
   e_object_unref(E_OBJECT(ev->zone));
   free(ev);
}

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

static int
_e_client_action_input_win_del(void)
{
   if (!comp_grabbed) return 0;

   comp_grabbed = 0;
   E_FREE_FUNC(action_rect, evas_object_del);
   e_comp_shape_queue();
   e_comp_ungrab_input(1, 1);
   return 1;
}

static void
_e_client_action_finish(void)
{
   if (comp_grabbed)
     _e_client_action_input_win_del();

   if (action_handler_key && action_client)
     evas_object_freeze_events_set(action_client->frame, 0);
   E_FREE_FUNC(action_timer, ecore_timer_del);
   E_FREE_FUNC(action_handler_key,  ecore_event_handler_del);
   E_FREE_FUNC(action_handler_mouse, ecore_event_handler_del);
   if (action_client)
     {
        action_client->keyboard_resizing = 0;
        if (action_client->internal_elm_win)
          ecore_event_window_ignore_events(elm_win_window_id_get(action_client->internal_elm_win), 0);
     }
   action_client = NULL;
}

static void
_e_client_mouse_action_end(E_Client *ec)
{
   if (!ec->cur_mouse_action) return;
   if (ec->cur_mouse_action->func.end_mouse)
     ec->cur_mouse_action->func.end_mouse(E_OBJECT(ec), "", NULL);
   else if (ec->cur_mouse_action->func.end)
     ec->cur_mouse_action->func.end(E_OBJECT(ec), "");
   E_FREE_FUNC(ec->cur_mouse_action, e_object_unref);
}

static void
_e_client_revert_focus(E_Client *ec)
{
   E_Client *pec;
   E_Desk *desk;

   if (stopping) return;

   if (!ec->focused) return;
   if (!ec->zone) return;

   desk = e_desk_current_get(ec->zone);
   if (ec->desk == desk)
     evas_object_focus_set(ec->frame, 0);

   if (ec->stack.prev)
     {
        ec->stack.focus_skip = 1;
        pec = e_client_stack_active_adjust(ec);
        ec->stack.focus_skip = 0;
        if ((pec != ec) && (!pec->iconic))
          evas_object_focus_set(pec->frame, 1);
        else
          {
             if ((e_object_is_del(E_OBJECT(ec))) || (ec->iconic))
               {
                  Eina_Bool unlock = ec->lock_focus_out;
                  ec->lock_focus_out = 1;
                  pec = e_desk_last_focused_focus(desk);
                  ec->lock_focus_out = unlock;
               }
          }
     }
   else if ((ec->parent) &&
            (ec->parent->desk == desk) && (ec->parent->modal == ec))
     {
        evas_object_focus_set(ec->parent->frame, 1);
        if (e_config->raise_on_revert_focus)
          evas_object_raise(ec->parent->frame);
     }
   else if (e_config->focus_revert_on_hide_or_close)
     {
        Eina_Bool unlock = ec->lock_focus_out;
        ec->lock_focus_out = 1;
        pec = e_desk_last_focused_focus(desk);
        ec->lock_focus_out = unlock;
     }
   else if (e_config->focus_policy == E_FOCUS_MOUSE)
     {
        pec = e_client_under_pointer_get(desk, ec);
        if (pec)
          evas_object_focus_set(pec->frame, 1);
        /* no autoraise/revert here because it's probably annoying */
     }
}

static void
_e_client_free(E_Client *ec)
{
   if (ec->pixmap)
     {
        if (e_pixmap_free(ec->pixmap))
          e_pixmap_client_set(ec->pixmap, NULL);
        ec->pixmap = NULL;
     }

   if (ec->frame)
     {
        e_comp_object_redirected_set(ec->frame, 0);
        e_comp_object_render_update_del(ec->frame);
     }

   E_OBJECT(ec)->references++;
   if (ec->fullscreen)
     {
        ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec);
        if (!ec->desk->fullscreen_clients)
          e_comp_render_queue();
     }
   if (ec->new_client)
     e_comp->new_clients--;
   if (ec->e.state.profile.use)
     {
        e_client_desk_window_profile_wait_desk_set(ec, NULL);

        if (ec->e.state.profile.available_list)
          {
             int i;
             for (i = 0; i < ec->e.state.profile.num; i++)
               eina_stringshare_replace(&ec->e.state.profile.available_list[i], NULL);
             E_FREE(ec->e.state.profile.available_list);
          }

        ec->e.state.profile.num = 0;

        eina_stringshare_replace(&ec->e.state.profile.set, NULL);
        eina_stringshare_replace(&ec->e.state.profile.wait, NULL);
        eina_stringshare_replace(&ec->e.state.profile.name, NULL);
        ec->e.state.profile.wait_for_done = 0;
        ec->e.state.profile.use = 0;
     }

   if (ec->e.state.video_parent && ec->e.state.video_parent_client)
     {
        ec->e.state.video_parent_client->e.state.video_child =
          eina_list_remove(ec->e.state.video_parent_client->e.state.video_child, ec);
     }
   if (ec->e.state.video_child)
     {
        E_Client *tmp;

        EINA_LIST_FREE(ec->e.state.video_child, tmp)
          tmp->e.state.video_parent_client = NULL;
     }
   E_FREE_FUNC(ec->internal_elm_win, evas_object_del);
   E_FREE_FUNC(ec->desktop, efreet_desktop_free);
   E_FREE_FUNC(ec->post_job, ecore_idle_enterer_del);

   E_FREE_FUNC(ec->kill_timer, ecore_timer_del);
   E_FREE_LIST(ec->pending_resize, free);

   if (ec->remember)
     {
        E_Remember *rem;

        rem = ec->remember;
        ec->remember = NULL;
        e_remember_unuse(rem);
     }
   ec->group = eina_list_free(ec->group);
   ec->transients = eina_list_free(ec->transients);
   ec->stick_desks = eina_list_free(ec->stick_desks);

   E_FREE(ec->netwm.extra_types);
   eina_stringshare_replace(&ec->border.name, NULL);
   eina_stringshare_replace(&ec->bordername, NULL);
   eina_stringshare_replace(&ec->icccm.name, NULL);
   eina_stringshare_replace(&ec->icccm.class, NULL);
   eina_stringshare_replace(&ec->icccm.title, NULL);
   eina_stringshare_replace(&ec->icccm.icon_name, NULL);
   eina_stringshare_replace(&ec->icccm.machine, NULL);
   eina_stringshare_replace(&ec->icccm.window_role, NULL);
   if ((ec->icccm.command.argc > 0) && (ec->icccm.command.argv))
     {
        int i;

        for (i = 0; i < ec->icccm.command.argc; i++)
          free(ec->icccm.command.argv[i]);
        E_FREE(ec->icccm.command.argv);
     }
   eina_stringshare_replace(&ec->netwm.name, NULL);
   eina_stringshare_replace(&ec->netwm.icon_name, NULL);
   eina_stringshare_replace(&ec->internal_icon, NULL);
   eina_stringshare_replace(&ec->internal_icon_key, NULL);
   eina_stringshare_replace(&ec->uuid, NULL);

   focus_stack = eina_list_remove(focus_stack, ec);
   raise_stack = eina_list_remove(raise_stack, ec);

   e_hints_client_list_set();
   if (ec->e.state.profile.wait_desk)
     {
        e_object_delfn_del(E_OBJECT(ec->e.state.profile.wait_desk),
                           ec->e.state.profile.wait_desk_delfn);
        ec->e.state.profile.wait_desk_delfn = NULL;
        e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk));
     }
   if (ec->stack.prev) ec->stack.prev->stack.next = ec->stack.next;
   if (ec->stack.next) ec->stack.next->stack.prev = ec->stack.prev;

   ec->e.state.profile.wait_desk = NULL;
   evas_object_del(ec->frame);
   E_OBJECT(ec)->references--;

   free(ec);
}

static void
_e_client_del(E_Client *ec)
{
   E_Client *child;
   E_Client_Volume_Sink *sink;

   EINA_LIST_FREE(ec->sinks, sink)
     e_client_volume_sink_remove(ec, sink);
   for (child = ec->stack.next; child; child = child->stack.next)
     e_client_act_close_begin(child);
   ec->changed = 0;
   focus_stack = eina_list_remove(focus_stack, ec);
   raise_stack = eina_list_remove(raise_stack, ec);
   if (ec->exe_inst)
     {
        if (ec->exe_inst->phony && (eina_list_count(ec->exe_inst->clients) == 1))
          {
             if (e_exec_phony_del(ec->exe_inst))
               ec->exe_inst = NULL;
          }
        else
          {
             if (!ec->exe_inst->deleted)
               {
                  ec->exe_inst->clients = eina_list_remove(ec->exe_inst->clients, ec);
                  ec->exe_inst = NULL;
               }
          }
     }

   _e_client_mouse_action_end(ec);
   if (action_client == ec) _e_client_action_finish();
   e_pointer_type_pop(e_comp->pointer, ec, NULL);

   if (warp_client == ec)
     {
        E_FREE_FUNC(warp_timer, ecore_timer_del);
        warp_client = NULL;
     }

   if ((client_drag) && (client_drag->data == ec))
     {
        e_object_del(E_OBJECT(client_drag));
        client_drag = NULL;
     }
   if (ec->border_menu) e_menu_deactivate(ec->border_menu);
   if (!stopping)
     {
        e_client_comp_hidden_set(ec, 1);
        if (ec->frame) evas_object_pass_events_set(ec->frame, 1);
     }

   E_FREE_FUNC(ec->border_locks_dialog, e_object_del);
   E_FREE_FUNC(ec->border_remember_dialog, e_object_del);
   E_FREE_FUNC(ec->border_border_dialog, e_object_del);
   E_FREE_FUNC(ec->border_prop_dialog, e_object_del);
   E_FREE_FUNC(ec->color_editor, evas_object_del);
   e_int_client_menu_del(ec);
   E_FREE_FUNC(ec->raise_timer, ecore_timer_del);

   if (ec->internal_elm_win)
     evas_object_hide(ec->internal_elm_win);

   if (ec->focused)
     _e_client_revert_focus(ec);
   if (ec->frame) evas_object_focus_set(ec->frame, 0);

   E_FREE_FUNC(ec->ping_poller, ecore_poller_del);
   eina_hash_del_by_key(clients_hash[e_pixmap_type_get(ec->pixmap)], &ec->pixmap);
   /* must be called before parent/child clear */
   _e_client_hook_call(E_CLIENT_HOOK_DEL, ec);
   E_FREE(ec->comp_data);

   if ((!ec->new_client) && (!stopping))
     _e_client_event_simple(ec, E_EVENT_CLIENT_REMOVE);

   if (ec->parent)
     {
        ec->parent->transients = eina_list_remove(ec->parent->transients, ec);
        ec->parent = NULL;
     }
   EINA_LIST_FREE(ec->transients, child)
     child->parent = NULL;

   if (ec->leader)
     {
        ec->leader->group = eina_list_remove(ec->leader->group, ec);
        if (ec->leader->modal == ec)
          ec->leader->modal = NULL;
        ec->leader = NULL;
     }
   EINA_LIST_FREE(ec->group, child)
     child->leader = NULL;

   e_comp->clients = eina_list_remove(e_comp->clients, ec);
   if (ec->frame) e_comp_object_render_update_del(ec->frame);
}

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

static Eina_Bool
_e_client_cb_kill_timer(void *data)
{
   E_Client *ec = data;

// dont wait until it's hung -
//   if (ec->hung)
//     {
   if (ec->netwm.pid > 1)
     kill(ec->netwm.pid, SIGKILL);
//     }
   ec->kill_timer = NULL;
   return ECORE_CALLBACK_CANCEL;
}

static Eina_Bool
_e_client_cb_ping_poller(void *data)
{
   E_Client *ec;

   ec = data;
   if (ec->ping_ok)
     {
        if (ec->hung)
          {
             ec->hung = 0;
             evas_object_smart_callback_call(ec->frame, "unhung", NULL);
             E_FREE_FUNC(ec->kill_timer, ecore_timer_del);
          }
     }
   else
     {
        /* if time between last ping and now is greater
         * than half the ping interval... */
        if ((ecore_loop_time_get() - ec->ping) >
            ((e_config->ping_clients_interval *
              ecore_poller_poll_interval_get(ECORE_POLLER_CORE)) / 2.0))
          {
             if (!ec->hung)
               {
                  ec->hung = 1;
                  evas_object_smart_callback_call(ec->frame, "hung", NULL);
                  /* FIXME: if below dialog is up - hide it now */
               }
             if (ec->delete_requested)
               {
                  /* FIXME: pop up dialog saying app is hung - kill client, or pid */
                  e_client_act_kill_begin(ec);
               }
          }
     }
   ec->ping_poller = NULL;
   e_client_ping(ec);
   return ECORE_CALLBACK_CANCEL;
}

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

static int
_e_client_action_input_win_new(void)
{
   if (comp_grabbed)
     {
        CRI("DOUBLE COMP GRAB! ACK!!!!");
        return 1;
     }
   comp_grabbed = e_comp_grab_input(1, 1);
   if (!comp_grabbed) _e_client_action_input_win_del();
   return comp_grabbed;
}

static void
_e_client_action_event_grabber_mouse_up(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   Evas_Event_Mouse_Down *ev = event_info;
   E_Binding_Event_Mouse_Button ev2;

   if (!action_client) return;
   e_bindings_evas_event_mouse_button_convert(ev, &ev2);
   e_client_mouse_up(action_client, ev->button, &ev->output, &ev2);
   if (!action_client) return;
   if (action_client->moving)
     e_client_act_move_end(action_client, NULL);
   if (!action_client) return;
   if (e_client_util_resizing_get(action_client))
     e_client_act_resize_end(action_client, NULL);
}

static void
_e_client_action_event_grabber_mouse_move(void *data EINA_UNUSED, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info)
{
   Evas_Event_Mouse_Move *ev = event_info;
   if (action_client)
     e_client_mouse_move(action_client, &ev->cur.output);
}

static void
_e_client_action_event_grabber_init(E_Client *ec)
{
   action_rect = e_comp_canvas_event_grabber_add();
   evas_object_event_callback_add(action_rect, EVAS_CALLBACK_MOUSE_UP, _e_client_action_event_grabber_mouse_up, NULL);
   evas_object_event_callback_add(action_rect, EVAS_CALLBACK_MOUSE_MOVE, _e_client_action_event_grabber_mouse_move, NULL);
   evas_object_smart_member_add(ec->frame, action_rect);
   evas_object_resize(action_rect, e_comp->w, e_comp->h);
   evas_object_layer_set(action_rect, EVAS_LAYER_MAX - 100);
   evas_object_show(action_rect);
   evas_object_event_grabber_freeze_when_visible_set(action_rect, 1);
   e_comp_shape_queue();
}

static void
_e_client_action_init(E_Client *ec)
{
   action_orig.x = ec->x;
   action_orig.y = ec->y;
   action_orig.w = ec->w;
   action_orig.h = ec->h;

   if (action_client)
     {
        action_client->keyboard_resizing = 0;
        if (action_client->internal_elm_win)
          ecore_event_window_ignore_events(elm_win_window_id_get(action_client->internal_elm_win), 0);
     }
   action_client = ec;
   if (ec->internal_elm_win)
     ecore_event_window_ignore_events(elm_win_window_id_get(ec->internal_elm_win), 1);
}

static void
_e_client_action_restore_orig(E_Client *ec)
{
   if (action_client != ec)
     return;

   evas_object_geometry_set(ec->frame, action_orig.x, action_orig.y, action_orig.w, action_orig.h);
}

static int
_e_client_key_down_modifier_apply(int modifier, int value)
{
   if (modifier & ECORE_EVENT_MODIFIER_CTRL) return value * 5;
   else if (modifier & ECORE_EVENT_MODIFIER_ALT)
     {
        value /= 5;
        if (value) return value;
        else return 1;
     }

   return value;
}


static int
_e_client_move_begin(E_Client *ec)
{
   if ((ec->fullscreen) || (ec->lock_user_location))
     return 0;

/*
   if (client_grabbed && !e_grabinput_get(e_client_util_pwin_get(ec), 0, e_client_util_pwin_get(ec)))
     {
        client_grabbed = 0;
        return 0;
     }
*/
   if (!_e_client_action_input_win_new()) return 0;
   ec->moving = 1;
   ecmove = ec;
   if (!ec->lock_user_stacking)
     {
        if (e_config->border_raise_on_mouse_action)
          evas_object_raise(ec->frame);
     }

   _e_client_hook_call(E_CLIENT_HOOK_MOVE_BEGIN, ec);
   if (!ec->moving)
     {
        if (ecmove == ec) ecmove = NULL;
        _e_client_action_input_win_del();
        return 0;
     }
   E_FREE_FUNC(ec->raise_timer, ecore_timer_del);
   _e_client_action_event_grabber_init(ec);
   return 1;
}

static int
_e_client_move_end(E_Client *ec)
{
   //if (client_grabbed)
     //{
        //e_grabinput_release(e_client_util_pwin_get(ec), e_client_util_pwin_get(ec));
        //client_grabbed = 0;
     //}
   _e_client_action_input_win_del();
   e_pointer_mode_pop(ec, E_POINTER_MOVE);
   ec->moving = 0;
   _e_client_hook_call(E_CLIENT_HOOK_MOVE_END, ec);

   ecmove = NULL;
   return 1;
}

static Eina_Bool
_e_client_action_move_timeout(void *data EINA_UNUSED)
{
   _e_client_move_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_CANCEL;
}

static void
_e_client_action_move_timeout_add(void)
{
   double timeout = e_config->border_keyboard.timeout;
   E_FREE_FUNC(action_timer, ecore_timer_del);
   if (!EINA_DBL_NONZERO(timeout)) timeout = 5.0;
   action_timer = ecore_timer_loop_add(timeout, _e_client_action_move_timeout, NULL);
}

static Eina_Bool
_e_client_move_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   Ecore_Event_Key *ev = event;
   int x, y, dx, dy;

   if (!comp_grabbed) return ECORE_CALLBACK_RENEW;
   if (!action_client)
     {
        ERR("no action_client!");
        goto stop;
     }

   x = action_client->x;
   y = action_client->y;

   dx = e_config->border_keyboard.move.dx;
   dx = _e_client_key_down_modifier_apply(ev->modifiers, dx);
   dy = e_config->border_keyboard.move.dy;
   dy = _e_client_key_down_modifier_apply(ev->modifiers, dy);

   switch (e_util_key_geometry_action_get(ev->key, &x, &y, dx, dy))
     {
      case E_UTIL_ACTION_DONE:
        goto stop;
        break;
      case E_UTIL_ACTION_ABORT:
        _e_client_action_restore_orig(action_client);
        goto stop;
        break;
      case E_UTIL_ACTION_DO:
        evas_object_move(action_client->frame, x, y);
        _e_client_action_move_timeout_add();
        break;
      case E_UTIL_ACTION_NONE:
      default:
        break;
     }

   return ECORE_CALLBACK_PASS_ON;

stop:
   _e_client_move_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_DONE;
}

static Eina_Bool
_e_client_move_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   if (!comp_grabbed) return ECORE_CALLBACK_RENEW;

   if (!action_client)
     ERR("no action_client!");

   _e_client_move_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_DONE;
}

static void
_e_client_moveinfo_gather(E_Client *ec, const char *source)
{
   if (e_util_glob_match(source, "mouse,*,1"))
     ec->moveinfo.down.button = 1;
   else if (e_util_glob_match(source, "mouse,*,2"))
     ec->moveinfo.down.button = 2;
   else if (e_util_glob_match(source, "mouse,*,3"))
     ec->moveinfo.down.button = 3;
   else
     ec->moveinfo.down.button = 0;
   if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3))
     {
        ec->moveinfo.down.mx = ec->mouse.last_down[ec->moveinfo.down.button - 1].mx;
        ec->moveinfo.down.my = ec->mouse.last_down[ec->moveinfo.down.button - 1].my;
     }
   else
     {
        ec->moveinfo.down.mx = ec->mouse.current.mx;
        ec->moveinfo.down.my = ec->mouse.current.my;
     }
}

static void
_e_client_resize_handle(E_Client *ec)
{
   int x, y, w, h;
   int new_x, new_y, new_w, new_h;
   int tw, th;
   Eina_List *skiplist = NULL;

   if (e_comp->updating) return;
   x = ec->x;
   y = ec->y;
   w = ec->w;
   h = ec->h;

   if ((ec->resize_mode == E_POINTER_RESIZE_TR) ||
       (ec->resize_mode == E_POINTER_RESIZE_R) ||
       (ec->resize_mode == E_POINTER_RESIZE_BR))
     {
        if ((ec->moveinfo.down.button >= 1) &&
            (ec->moveinfo.down.button <= 3))
          w = ec->mouse.last_down[ec->moveinfo.down.button - 1].w +
            (ec->mouse.current.mx - ec->moveinfo.down.mx);
        else
          w = ec->moveinfo.down.w + (ec->mouse.current.mx - ec->moveinfo.down.mx);
     }
   else if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
            (ec->resize_mode == E_POINTER_RESIZE_L) ||
            (ec->resize_mode == E_POINTER_RESIZE_BL))
     {
        if ((ec->moveinfo.down.button >= 1) &&
            (ec->moveinfo.down.button <= 3))
          w = ec->mouse.last_down[ec->moveinfo.down.button - 1].w -
            (ec->mouse.current.mx - ec->moveinfo.down.mx);
        else
          w = ec->moveinfo.down.w - (ec->mouse.current.mx - ec->moveinfo.down.mx);
     }

   if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
       (ec->resize_mode == E_POINTER_RESIZE_T) ||
       (ec->resize_mode == E_POINTER_RESIZE_TR))
     {
        if ((ec->moveinfo.down.button >= 1) &&
            (ec->moveinfo.down.button <= 3))
          h = ec->mouse.last_down[ec->moveinfo.down.button - 1].h -
            (ec->mouse.current.my - ec->moveinfo.down.my);
        else
          h = ec->moveinfo.down.h - (ec->mouse.current.my - ec->moveinfo.down.my);
     }
   else if ((ec->resize_mode == E_POINTER_RESIZE_BL) ||
            (ec->resize_mode == E_POINTER_RESIZE_B) ||
            (ec->resize_mode == E_POINTER_RESIZE_BR))
     {
        if ((ec->moveinfo.down.button >= 1) &&
            (ec->moveinfo.down.button <= 3))
          h = ec->mouse.last_down[ec->moveinfo.down.button - 1].h +
            (ec->mouse.current.my - ec->moveinfo.down.my);
        else
          h = ec->moveinfo.down.h + (ec->mouse.current.my - ec->moveinfo.down.my);
     }

   tw = ec->w;
   th = ec->h;

   if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
       (ec->resize_mode == E_POINTER_RESIZE_L) ||
       (ec->resize_mode == E_POINTER_RESIZE_BL))
     x += (tw - w);
   if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
       (ec->resize_mode == E_POINTER_RESIZE_T) ||
       (ec->resize_mode == E_POINTER_RESIZE_TR))
     y += (th - h);

   skiplist = eina_list_append(skiplist, ec);
   e_resist_client_position(skiplist,
                                      ec->x, ec->y, ec->w, ec->h,
                                      x, y, w, h,
                                      &new_x, &new_y, &new_w, &new_h);
   eina_list_free(skiplist);

   w = new_w;
   h = new_h;
   if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE)
     {
        if (ec->zone)
          {
             w = MIN(w, ec->zone->w);
             h = MIN(h, ec->zone->h);
          }
     }
   e_client_resize_limit(ec, &new_w, &new_h);
   if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
       (ec->resize_mode == E_POINTER_RESIZE_L) ||
       (ec->resize_mode == E_POINTER_RESIZE_BL))
     new_x += (w - new_w);
   if ((ec->resize_mode == E_POINTER_RESIZE_TL) ||
       (ec->resize_mode == E_POINTER_RESIZE_T) ||
       (ec->resize_mode == E_POINTER_RESIZE_TR))
     new_y += (h - new_h);

   evas_object_geometry_set(ec->frame, new_x, new_y, new_w, new_h);
}

static int
_e_client_resize_end(E_Client *ec)
{
   _e_client_action_input_win_del();
   e_pointer_mode_pop(ec, ec->resize_mode);
   ec->resize_mode = E_POINTER_RESIZE_NONE;

   /* If this border was maximized, we need to unset Maximized state or
    * on restart, E still thinks it's maximized */
   if (ec->maximized != E_MAXIMIZE_NONE)
     e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL,
                                  ec->maximized & E_MAXIMIZE_VERTICAL);

   _e_client_hook_call(E_CLIENT_HOOK_RESIZE_END, ec);

   ecresize = NULL;

   return 1;
}


static Eina_Bool
_e_client_action_resize_timeout(void *data EINA_UNUSED)
{
   _e_client_resize_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_CANCEL;
}

static void
_e_client_action_resize_timeout_add(void)
{
   double timeout = e_config->border_keyboard.timeout;
   E_FREE_FUNC(action_timer, ecore_timer_del);
   if (!EINA_DBL_NONZERO(timeout)) timeout = 5.0;
   action_timer = ecore_timer_loop_add(timeout, _e_client_action_resize_timeout, NULL);
}

static Eina_Bool
_e_client_resize_key_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event)
{
   Ecore_Event_Key *ev = event;
   int w, h, dx, dy;

   if (!comp_grabbed) return ECORE_CALLBACK_RENEW;
   if (!action_client)
     {
        ERR("no action_client!");
        goto stop;
     }

   w = action_client->w;
   h = action_client->h;

   dx = e_config->border_keyboard.resize.dx;
   if (dx < action_client->icccm.step_w) dx = action_client->icccm.step_w;
   dx = _e_client_key_down_modifier_apply(ev->modifiers, dx);
   if (dx < action_client->icccm.step_w) dx = action_client->icccm.step_w;

   dy = e_config->border_keyboard.resize.dy;
   if (dy < action_client->icccm.step_h) dy = action_client->icccm.step_h;
   dy = _e_client_key_down_modifier_apply(ev->modifiers, dy);
   if (dy < action_client->icccm.step_h) dy = action_client->icccm.step_h;

   switch (e_util_key_geometry_action_get(ev->key, &w, &h, dx, dy))
     {
      case E_UTIL_ACTION_DONE:
        goto stop;
        break;
      case E_UTIL_ACTION_ABORT:
        _e_client_action_restore_orig(action_client);
        goto stop;
        break;
      case E_UTIL_ACTION_DO:
        if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE)
          {
             if (action_client->zone)
               {
                  w = MIN(w, action_client->zone->w);
                  h = MIN(h, action_client->zone->h);
               }
          }
        e_client_resize_limit(action_client, &w, &h);
        evas_object_resize(action_client->frame, w, h);
        _e_client_action_resize_timeout_add();
        break;
      case E_UTIL_ACTION_NONE:
      default:
        break;
     }

   return ECORE_CALLBACK_PASS_ON;

stop:
   _e_client_resize_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_DONE;
}

static Eina_Bool
_e_client_resize_mouse_down(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   if (!comp_grabbed) return ECORE_CALLBACK_RENEW;

   if (!action_client)
     ERR("no action_client!");

   _e_client_resize_end(action_client);
   _e_client_action_finish();
   return ECORE_CALLBACK_DONE;
}

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

static E_Client *
_e_client_under_pointer_helper(E_Desk *desk, E_Client *exclude, int x, int y)
{
   E_Client *ec = NULL, *cec;

   E_CLIENT_REVERSE_FOREACH(cec)
     {
        /* If a border was specified which should be excluded from the list
         * (because it will be closed shortly for example), skip */
        if (e_client_util_ignored_get(cec) || (!e_client_util_desk_visible(cec, desk))) continue;
        if (!evas_object_visible_get(cec->frame)) continue;
        if ((exclude) && (cec == exclude)) continue;
        if (!E_INSIDE(x, y, cec->x, cec->y, cec->w, cec->h))
          continue;
        /* If the layer is higher, the position of the window is higher
         * (always on top vs always below) */
        if (!ec || (cec->layer > ec->layer))
          ec = cec;
     }
   return ec;
}

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

static void
_e_client_zones_layout_calc(E_Client *ec, int *zx, int *zy, int *zw, int *zh)
{
   int x, y, w, h;
   E_Zone *zone_above, *zone_below, *zone_left, *zone_right;

   if (!ec->zone) return;
   x = ec->zone->x;
   y = ec->zone->y;
   w = ec->zone->w;
   h = ec->zone->h;

   if (eina_list_count(e_comp->zones) == 1)
     {
        if (zx) *zx = x;
        if (zy) *zy = y;
        if (zw) *zw = w;
        if (zh) *zh = h;
        return;
     }

   zone_left = e_comp_zone_xy_get((x - w + 5), y);
   zone_right = e_comp_zone_xy_get((x + w + 5), y);
   zone_above = e_comp_zone_xy_get(x, (y - h + 5));
   zone_below = e_comp_zone_xy_get(x, (y + h + 5));

   if (!(zone_above) && (y))
     zone_above = e_comp_zone_xy_get(x, (h - 5));

   if (!(zone_left) && (x))
     zone_left = e_comp_zone_xy_get((x - 5), y);

   if (zone_right)
     w = zone_right->x + zone_right->w;

   if (zone_left)
     w = ec->zone->x + ec->zone->w;

   if (zone_below)
     h = zone_below->y + zone_below->h;

   if (zone_above)
     h = ec->zone->y + ec->zone->h;

   if ((zone_left) && (zone_right))
     w = ec->zone->w + zone_right->x;

   if ((zone_above) && (zone_below))
     h = ec->zone->h + zone_below->y;

   if (x) x -= ec->zone->w;
   if (y) y -= ec->zone->h;

   if (zx) *zx = x > 0 ? x : 0;
   if (zy) *zy = y > 0 ? y : 0;
   if (zw) *zw = w;
   if (zh) *zh = h;
}

static void
_e_client_stay_within_canvas(E_Client *ec, int x, int y, int *new_x, int *new_y)
{
   int new_x_max, new_y_max;
   int zw, zh;
   Eina_Bool lw, lh;

   if (!ec->zone)
     {
        if (new_x) *new_x = x;
        if (new_y) *new_y = y;
        return;
     }

   _e_client_zones_layout_calc(ec, NULL, NULL, &zw, &zh);

   new_x_max = zw - ec->w;
   new_y_max = zh - ec->h;
   lw = ec->w > zw ? EINA_TRUE : EINA_FALSE;
   lh = ec->h > zh ? EINA_TRUE : EINA_FALSE;

   if (lw)
     {
        if (x <= new_x_max)
          *new_x = new_x_max;
        else if (x >= 0)
          *new_x = 0;
     }
   else
     {
        if (x >= new_x_max)
          *new_x = new_x_max;
        else if (x <= 0)
          *new_x = 0;
     }

   if (lh)
     {
        if (y <= new_y_max)
          *new_y = new_y_max;
        else if (y >= 0)
          *new_y = 0;
     }
   else
     {
        if (y >= new_y_max)
          *new_y = new_y_max;
        else if (y <= 0)
          *new_y = 0;
     }
}

static void
_e_client_reset_lost_window(E_Client *ec)
{
   E_OBJECT_CHECK(ec);

   if (ec->during_lost) return;
   ec->during_lost = EINA_TRUE;

   if (ec->iconic) e_client_uniconify(ec);
   if (!ec->moving) e_comp_object_util_center(ec->frame);

   evas_object_raise(ec->frame);
   if (!ec->lock_focus_out)
     evas_object_focus_set(ec->frame, 1);

   e_client_pointer_warp_to_center(ec);
   ec->during_lost = EINA_FALSE;
}

static void
_e_client_move_lost_window_to_center(E_Client *ec)
{
   int loss_overlap = 5;
   int zw, zh, zx, zy;

   if (ec->during_lost) return;
   if (!ec->zone) return;

   _e_client_zones_layout_calc(ec, &zx, &zy, &zw, &zh);

   if (!E_INTERSECTS(zx + loss_overlap,
                     zy + loss_overlap,
                     zw - (2 * loss_overlap),
                     zh - (2 * loss_overlap),
                     ec->x, ec->y, ec->w, ec->h))
     {
        if (e_config->edge_flip_dragging)
          {
             Eina_Bool lf, rf, tf, bf;

             lf = rf = tf = bf = EINA_TRUE;

             if (ec->zone->desk_x_count <= 1) lf = rf = EINA_FALSE;
             else if (!e_config->desk_flip_wrap)
               {
                  if (ec->zone->desk_x_current == 0) lf = EINA_FALSE;
                  if (ec->zone->desk_x_current == (ec->zone->desk_x_count - 1)) rf = EINA_FALSE;
               }

             if (ec->zone->desk_y_count <= 1) tf = bf = EINA_FALSE;
             else if (!e_config->desk_flip_wrap)
               {
                  if (ec->zone->desk_y_current == 0) tf = EINA_FALSE;
                  if (ec->zone->desk_y_current == (ec->zone->desk_y_count - 1)) bf = EINA_FALSE;
               }

             if (!(lf) && (ec->x <= loss_overlap) && !(ec->zone->flip.switching))
               _e_client_reset_lost_window(ec);

             if (!(rf) && (ec->x >= (ec->zone->w - loss_overlap)) && !(ec->zone->flip.switching))
               _e_client_reset_lost_window(ec);

             if (!(tf) && (ec->y <= loss_overlap) && !(ec->zone->flip.switching))
               _e_client_reset_lost_window(ec);

             if (!(bf) && (ec->y >= (ec->zone->h - loss_overlap)) && !(ec->zone->flip.switching))
               _e_client_reset_lost_window(ec);
          }

        if (!e_config->edge_flip_dragging)
          _e_client_reset_lost_window(ec);
     }
}

////////////////////////////////////////////////
static void
_e_client_zone_update(E_Client *ec)
{
   Eina_List *l;
   E_Zone *zone;

   /* still within old zone - leave it there */
   if (ec->zone && E_INTERSECTS(ec->x, ec->y, ec->w, ec->h,
                    ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h))
     return;
   /* find a new zone */
   EINA_LIST_FOREACH(e_comp->zones, l, zone)
     {
        if (E_INTERSECTS(ec->x, ec->y, ec->w, ec->h,
                         zone->x, zone->y, zone->w, zone->h))
          {
             e_client_zone_set(ec, zone);
             return;
          }
     }
}

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

E_API Eina_List *
e_client_stack_list_prepare(E_Client *ec)
{
   E_Client *ec2;
   Eina_List *list = NULL;

   for (ec2 = ec->stack.prev; ec2; ec2 = ec2->stack.prev)
     {
        ec2->stack.ignore++;
        list = eina_list_prepend(list, ec2);
     }
   ec->stack.ignore++;
   list = eina_list_append(list, ec);
   for (ec2 = ec->stack.next; ec2; ec2 = ec2->stack.next)
     {
        ec2->stack.ignore++;
        list = eina_list_append(list, ec2);
     }
   return list;
}

E_API void
e_client_stack_list_finish(Eina_List *list)
{
   E_Client *ec;

   EINA_LIST_FREE(list, ec) ec->stack.ignore--;
}

E_API E_Client *
e_client_stack_top_get(E_Client *ec)
{
   E_Client *ec2;

   for (ec2 = ec; ec2; ec2 = ec2->stack.next)
     {
        if (!ec2->stack.next) return ec2;
     }
   return ec;
}

E_API E_Client *
e_client_stack_bottom_get(E_Client *ec)
{
   E_Client *ec2;

   for (ec2 = ec; ec2; ec2 = ec2->stack.prev)
     {
        if (!ec2->stack.prev) return ec2;
     }
   return ec;
}

E_API E_Client *
e_client_stack_active_adjust(E_Client *ec)
{
   E_Client *pec = ec;
   if ((!ec->stack.prev) && (!ec->stack.next)) return ec;
   ec = e_client_stack_top_get(ec);
   for (; ec; ec = ec->stack.prev)
     {
        if (e_object_is_del(E_OBJECT(ec))) continue;
        if (ec->stack.focus_skip) continue;
        if (ec->iconic) continue;
        if (ec->visible) break;
     }
   if (!ec) ec = pec;
   return ec;
}

E_API Eina_Bool
e_client_stack_focused_get(E_Client *ec)
{
  E_Client *ec2;

   ec2 = e_client_stack_bottom_get(ec);
   for (; ec2; ec2 = ec2->stack.next)
     {
        if (ec2->focused) return EINA_TRUE;
     }
   return EINA_FALSE;
}

E_API Eina_Bool
e_client_stack_iconified_get(E_Client *ec)
{
   E_Client *ec2;

   ec2 = e_client_stack_bottom_get(ec);
   for (; ec2; ec2 = ec2->stack.next)
     {
        if (ec2->iconic) return EINA_TRUE;
     }
   return EINA_FALSE;
}

E_API Eina_Bool
e_client_stack_urgent_get(E_Client *ec)
{
   E_Client *ec2;

   ec2 = e_client_stack_bottom_get(ec);
   for (; ec2; ec2 = ec2->stack.next)
     {
        if (ec2->urgent) return EINA_TRUE;
     }
   return EINA_FALSE;
}

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

static void
_e_client_cb_evas_hide(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Client *ec = data;

   if (stopping) return; //ignore all of this if we're shutting down!
   if (e_object_is_del(data)) return; //client is about to die
   _e_client_mouse_action_end(ec);
   if (action_client == ec) _e_client_action_finish();
   if (!evas_object_pass_events_get(ec->frame))
     e_pointer_type_pop(e_comp->pointer, ec, NULL);

   if (!ec->hidden)
     {
        if (ec->focused)
          _e_client_revert_focus(ec);
     }
   ec->want_focus = ec->take_focus = 0;

   ec->post_show = 0;

   if (ec->new_client || ec->iconic) return;
   _e_client_event_simple(ec, E_EVENT_CLIENT_HIDE);
}

static void
_e_client_cb_evas_shade_done(void *data, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Client *ec = data;

   ec->shading = 0;
   ec->shaded = !(ec->shaded);
   ec->changes.shaded = 1;
   ec->changes.shading = 1;
   e_client_comp_hidden_set(ec, ec->shaded);
}

static void
_e_client_cb_evas_move(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Client *ec = data;
   Evas_Coord x, y;

   ec->pre_res_change.valid = 0;
   if (ec->internal_elm_win)
     {
        EC_CHANGED(ec);
        ec->changes.pos = 1;
     }

   _e_client_event_simple(ec, E_EVENT_CLIENT_MOVE);

   if (!ec->ignored) _e_client_zone_update(ec);
   evas_object_geometry_get(ec->frame, &x, &y, NULL, NULL);
   if (ec->stack.prev || ec->stack.next)
     {
        // do nothing - handled by idle enterer eval
     }
   else
     {
        if ((e_config->transient.move) && (ec->transients))
          {
             Eina_List *list = eina_list_clone(ec->transients);
             E_Client *child;

             EINA_LIST_FREE(list, child)
               {
                  if (child->placed)
                    evas_object_move(child->frame,
                                     child->x + x - ec->pre_cb.x,
                                     child->y + y - ec->pre_cb.y);
               }
          }
     }
   if (ec->moving || (ecmove == ec))
     _e_client_hook_call(E_CLIENT_HOOK_MOVE_UPDATE, ec);
   e_remember_update(ec);
   if (ec->fullscreen || (ec->maximized & E_MAXIMIZE_DIRECTION))
     e_hints_window_size_set(ec);
   ec->pre_cb.x = x; ec->pre_cb.y = y;
}

static void
_e_client_cb_evas_resize(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Client *ec = data;
   Evas_Coord x, y, w, h;

   ec->pre_res_change.valid = 0;

   _e_client_event_simple(ec, E_EVENT_CLIENT_RESIZE);

   evas_object_geometry_get(ec->frame, &x, &y, &w, &h);
   if (ec->stack.prev || ec->stack.next)
     {
        // do nothing - handled by idle enterer eval
     }
   else
     {
        if ((e_config->transient.resize) && (ec->transients))
          {
             Eina_List *list = eina_list_clone(ec->transients);
             E_Client *child;

             EINA_LIST_FREE(list, child)
               {
                  Evas_Coord nx, ny, nw, nh;

                  if (!child->placed) continue;

                  if ((ec->pre_cb.w > 0) && (ec->pre_cb.h > 0))
                    {
                       nx = x + (((child->x - x) * w) / ec->pre_cb.w);
                       ny = y + (((child->y - y) * h) / ec->pre_cb.h);
                       nw = (child->w * w) / ec->pre_cb.w;
                       nh = (child->h * h) / ec->pre_cb.h;
                       nx += ((nw - child->w) / 2);
                       ny += ((nh - child->h) / 2);
                       evas_object_move(child->frame, nx, ny);
                    }
               }
          }
     }

   if (e_client_util_resizing_get(ec) || (ecresize == ec))
     _e_client_hook_call(E_CLIENT_HOOK_RESIZE_UPDATE, ec);
   e_remember_update(ec);
   if (ec->fullscreen || (ec->maximized & E_MAXIMIZE_DIRECTION))
     e_hints_window_size_set(ec);
   ec->pre_cb.w = w; ec->pre_cb.h = h;
}

static void
_e_client_cb_evas_show(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   _e_client_event_simple(data, E_EVENT_CLIENT_SHOW);
}

static void
_e_client_cb_evas_restack(void *data, Evas *e EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event_info EINA_UNUSED)
{
   E_Client *ec = data;

   if (ec->layer_block) return;
   if (ec->stack.prev || ec->stack.next)
     {
        if (ec->stack.ignore == 0)
          {
             Eina_List *l, *list = e_client_stack_list_prepare(ec);
             E_Client *child;

             EINA_LIST_FOREACH(list, l, child)
               {
                  if (child == ec) break;
                  evas_object_stack_below(child->frame, ec->frame);
               }
             EINA_LIST_REVERSE_FOREACH(list, l, child)
               {
                  if (child == ec) break;
                  evas_object_stack_above(child->frame, ec->frame);
               }
             e_client_stack_list_finish(list);
          }
     }
   else
     {
        if (e_config->transient.raise && ec->transients)
          e_client_transients_restack(ec);
     }
   if (ec->unredirected_single) return;
   e_remember_update(ec);
   _e_client_event_simple(ec, E_EVENT_CLIENT_STACK);
}

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

static void
_e_client_maximize_done(void *data, E_Efx_Map_Data *emd EINA_UNUSED, Evas_Object *obj)
{
   E_Client *ec = data;
   ec->maximize_override = 0;
   ec->agent = NULL;
   evas_object_del(obj);
}

static Eina_Bool
_e_client_maximize_run(E_Client *ec, int x, int y, int w, int h)
{
   int pw, ph;
   Eina_Bool disabled = EINA_FALSE;
   if (e_pixmap_size_get(ec->pixmap, &pw, &ph))
      {
         e_comp_object_frame_wh_adjust(ec->frame, pw, ph, &pw, &ph);
         disabled = (w == pw) && (h == ph);
      }
   if ((!disabled) && e_config->window_maximize_animate && (!ec->maximize_anims_disabled) &&
       (!starting) && (!ec->changes.need_maximize))
     {
        evas_object_del(ec->agent);
        ec->agent = e_comp_object_agent_add(ec->frame);
        e_efx_resize(ec->agent, e_config->window_maximize_transition, E_EFX_POINT(x, y),
          w, h, e_config->window_maximize_time, _e_client_maximize_done, ec);
        return EINA_TRUE;
     }
   evas_object_geometry_set(ec->frame, x, y, w, h);
   return EINA_FALSE;
}

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

static void
_e_client_eval(E_Client *ec)
{
   int rem_change = 0;
   int send_event = 1;
   unsigned int prop = 0;
   int zx = 0, zy = 0, zw = 0, zh = 0;


   if (e_object_is_del(E_OBJECT(ec)))
     {
        CRI("_e_client_eval(%p) with deleted border! - %d\n", ec, ec->new_client);
        ec->changed = 0;
        return;
     }

   if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_NEW_CLIENT, ec)) return;

   if ((ec->new_client) && (!e_client_util_ignored_get(ec)) && (ec->zone))
     {
        _e_client_event_simple(ec, E_EVENT_CLIENT_ADD);
        e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh);
        /* enforce wm size hints for initial sizing */
        if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE)
          {
             ec->w = MIN(ec->w, ec->zone->w);
             ec->h = MIN(ec->h, ec->zone->h);
          }
        e_client_resize_limit(ec, &ec->w, &ec->h);

        if (ec->re_manage && e_comp_object_frame_exists(ec->frame))
          {
             int x = ec->x, y = ec->y;
             if (ec->x) e_comp_object_frame_xy_adjust(ec->frame, ec->x, 0, &ec->x, NULL);
             if (ec->y) e_comp_object_frame_xy_adjust(ec->frame, 0, ec->y, NULL, &ec->y);
             if ((x != ec->x) || (y != ec->y)) ec->changes.pos = 1;
             ec->placed = 1;
             ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y;
          }

        if ((ec->stack.prev) && (!ec->dialog))
          {
             E_Client *ec2 = e_client_stack_bottom_get(ec);

             ec->stack.ignore++;
             evas_object_move(ec->frame, ec2->x, ec2->y);
             evas_object_resize(ec->frame, ec2->w, ec2->h);
             ec->stack.ignore--;
          }
     }
   if (ec->new_client && (!ec->override))
     e_hints_window_init(ec);
   if ((!e_client_util_ignored_get(ec)) && ec->zone && ec->visible && (!ec->placed))
     {
        e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh);
        if (ec->parent)
          {
             Eina_Bool centered = EINA_FALSE;
             if (ec->parent->zone != e_zone_current_get())
               {
                  e_client_zone_set(ec, ec->parent->zone);
                  e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh);
               }

             if (evas_object_visible_get(ec->parent->frame))
               {
                  if ((!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh)) ||
                     (!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, ec->parent->x, ec->parent->y, ec->parent->w, ec->parent->h)))
                    {
                       int x, y;

                       e_comp_object_util_center_pos_get(ec->parent->frame, &x, &y);
                       if (E_CONTAINS(x, y, ec->w, ec->h, zx, zy, zw, zh))
                         {
                            ec->x = x, ec->y = y;
                         }
                       else
                         {
                            x = ec->parent->x;
                            y = ec->parent->y;
                            if (!E_CONTAINS(x, y, ec->w, ec->h, zx, zy, zw, zh))
                              {
                                 e_comp_object_util_center_on(ec->frame,
                                                              ec->parent->frame);
                                 centered = 1;
                              }
                         }
                       ec->changes.pos = 1;
                    }
               }
             else
               {
                  e_comp_object_util_center_on(ec->frame,
                                               ec->parent->frame);
                  centered = 1;
               }
             if (centered) //test for offscreen
               {
                  if (!E_CONTAINS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh))
                    {
                       if (ec->x < zx)
                         ec->x = ec->parent->x;
                       if (ec->y < zy)
                         ec->y = ec->parent->y;
                       if (ec->x + ec->w > zx + zw)
                         ec->x = ec->parent->x + ec->parent->w - ec->w;
                       if (ec->y + ec->h > zy + zh)
                         ec->y = ec->parent->y + ec->parent->h - ec->h;
                       ec->changes.pos = 1;
                    }
               }
             ec->placed = 1;
             ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y;
          }
#if 0
        else if ((ec->leader) && (ec->dialog))
          {
             /* TODO: Place in center of group */
          }
#endif
        else if (ec->dialog)
          {
             E_Client *trans_ec = NULL;

             if (ec->icccm.transient_for)
               trans_ec = e_pixmap_find_client(E_PIXMAP_TYPE_X, ec->icccm.transient_for);
             if (trans_ec)
               {
                  // if transient for a window and not placed, center on
                  // transient parent if found
                  ec->x = trans_ec->x + ((trans_ec->w - ec->w) / 2);
                  ec->y = trans_ec->y + ((trans_ec->h - ec->h) / 2);
               }
             else
               {
                  ec->x = zx + ((zw - ec->w) / 2);
                  ec->y = zy + ((zh - ec->h) / 2);
               }
             ec->changes.pos = 1;
             ec->placed = 1;
             ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y;
          }

        if (!ec->placed)
          {
             Eina_List *skiplist = NULL;
             int new_x, new_y, t = 0;
             E_Client *trans_ec = NULL;

             if (zw > ec->w)
               new_x = zx + (rand() % (zw - ec->w));
             else
               new_x = zx;
             if (zh > ec->h)
               new_y = zy + (rand() % (zh - ec->h));
             else
               new_y = zy;

             e_comp_object_frame_geometry_get(ec->frame, NULL, NULL, &t, NULL);

             if (ec->icccm.transient_for)
               trans_ec = e_pixmap_find_client(E_PIXMAP_TYPE_X, ec->icccm.transient_for);
             if (trans_ec)
               {
                  // if transient for a window and not placed, center on
                  // transient parent if found
                  new_x = trans_ec->x + ((trans_ec->w - ec->w) / 2);
                  new_y = trans_ec->y + ((trans_ec->h - ec->h) / 2);
               }
             else if ((e_config->window_placement_policy == E_WINDOW_PLACEMENT_SMART) || (e_config->window_placement_policy == E_WINDOW_PLACEMENT_ANTIGADGET))
               {
                  skiplist = eina_list_append(skiplist, ec);
                  if (ec->transients)
                    skiplist = eina_list_merge(skiplist, eina_list_clone(ec->transients));
                  if (ec->desk)
                    e_place_desk_region_smart(ec->desk, skiplist,
                                              ec->x, ec->y, ec->w, ec->h,
                                              &new_x, &new_y);
                  else
                    e_place_zone_region_smart(ec->zone, skiplist,
                                              ec->x, ec->y, ec->w, ec->h,
                                              &new_x, &new_y);
                  eina_list_free(skiplist);
               }
             else if (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL)
               {
                  e_place_zone_manual(ec->zone, ec->w, t, &new_x, &new_y);
               }
             else
               {
                  e_place_zone_cursor(ec->zone, ec->x, ec->y, ec->w, ec->h,
                                      t, &new_x, &new_y);
               }
             ec->x = new_x;
             ec->y = new_y;
             ec->changes.pos = 1;
             ec->pre_cb.x = ec->x; ec->pre_cb.y = ec->y;
          }
        else if (!E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, zx, zy, zw, zh))
          {
             /* If an ec is placed out of bound, fix it! */
             ec->x = zx + ((zw - ec->w) / 2);
             ec->y = zy + ((zh - ec->h) / 2);
             ec->changes.pos = 1;
          }

        /* Recreate state */
        if ((ec->e.state.centered) &&
            ((!ec->remember) ||
             ((ec->remember) && (!(ec->remember->apply & E_REMEMBER_APPLY_POS)))))
          {
             ec->x = zx + (zw - ec->w) / 2;
             ec->y = zy + (zh - ec->h) / 2;
             ec->changes.pos = 1;
          }

        /* if the explicit geometry request asks for the app to be
         * in another zone - well move it there */
        {
           E_Zone *zone = NULL;
           int x, y;

           x = MAX(ec->x, 0);
           y = MAX(ec->y, 0);
           if ((!ec->re_manage) && ((ec->x != x) || (ec->y != y)))
             zone = e_comp_zone_xy_get(x, y);

           if (!zone)
             {
                zone = e_comp_zone_xy_get(ec->x + (ec->w / 2), ec->y + (ec->h / 2));
                if (zone)
                  {
                     E_Zone *z2 = e_comp_zone_xy_get(ec->x, ec->y);

                     if (z2 && (z2 != zone))
                       {
                          size_t psz = 0;
                          E_Zone *zf = z2;
                          Eina_List *l;

                          EINA_LIST_FOREACH(e_comp->zones, l, z2)
                            {
                                int w, h;

                                x = ec->x, y = ec->y, w = ec->w, h = ec->h;
                                E_RECTS_CLIP_TO_RECT(x, y, w, h, z2->x, z2->y, z2->w, z2->h);
                                if (w * h == z2->w * z2->h)
                                  {
                                     /* client fully covering zone */
                                     zf = z2;
                                     break;
                                  }
                                if ((unsigned)(w * h) > psz)
                                  {
                                     psz = w * h;
                                     zf = z2;
                                  }
                            }
                          zone = zf;
                       }
                  }
             }
           if (!zone)
             zone = e_comp_zone_xy_get(ec->x, ec->y);
           if (!zone)
             zone = e_comp_zone_xy_get(ec->x + ec->w - 1, ec->y);
           if (!zone)
             zone = e_comp_zone_xy_get(ec->x + ec->w - 1, ec->y + ec->h - 1);
           if (!zone)
             zone = e_comp_zone_xy_get(ec->x, ec->y + ec->h - 1);
           if ((zone) && (zone != ec->zone))
             e_client_zone_set(ec, zone);
        }
     }

   if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_NEW_CLIENT, ec)) return;

   /* effect changes to the window border itself */
   if ((ec->changes.shading))
     {
        /*  show at start of unshade (but don't hide until end of shade) */
        //if (ec->shaded)
          //ecore_x_window_raise(ec->win);
        ec->changes.shading = 0;
        send_event = 0;
        rem_change = 1;
     }
   if (ec->changes.shaded) send_event = 0;
   if ((ec->changes.shaded) && (ec->changes.pos) && (ec->changes.size))
     {
        //if (ec->shaded)
          //ecore_x_window_lower(ec->win);
        //else
          //ecore_x_window_raise(ec->win);
        ec->changes.shaded = 0;
        rem_change = 1;
     }
   else if ((ec->changes.shaded) && (ec->changes.pos))
     {
        //if (ec->shaded)
          //ecore_x_window_lower(ec->win);
        //else
          //ecore_x_window_raise(ec->win);
        ec->changes.size = 1;
        ec->changes.shaded = 0;
        rem_change = 1;
     }
   else if ((ec->changes.shaded) && (ec->changes.size))
     {
        //if (ec->shaded)
          //ecore_x_window_lower(ec->win);
        //else
          //ecore_x_window_raise(ec->win);
        ec->changes.shaded = 0;
        rem_change = 1;
     }
   else if (ec->changes.shaded)
     {
        //if (ec->shaded)
          //ecore_x_window_lower(ec->win);
        //else
          //ecore_x_window_raise(ec->win);
        ec->changes.shaded = 0;
        rem_change = 1;
     }

   if (ec->changes.size)
     {
        ec->changes.size = 0;
        if ((!ec->shaded) && (!ec->shading))
          evas_object_resize(ec->frame, ec->w, ec->h);

        rem_change = 1;
        prop |= E_CLIENT_PROPERTY_SIZE;
     }
   if (ec->changes.pos)
     {
        Eina_Bool placed = ec->placed;

        ec->changes.pos = 0;
        evas_object_move(ec->frame, ec->x, ec->y);
        if (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL)
          ec->placed = placed;
        rem_change = 1;
        prop |= E_CLIENT_PROPERTY_POS;
     }
   if (ec->changes.need_rescale)
     {
        e_client_rescale(ec);
        ec->changes.need_rescale = 0;
     }

   if (ec->changes.reset_gravity)
     {
        ec->changes.reset_gravity = 0;
        rem_change = 1;
        prop |= E_CLIENT_PROPERTY_GRAVITY;
     }

   if ((ec->changes.visible) && (ec->visible) && (ec->new_client) && (!ec->iconic))
     {
        int x, y;

        ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
        if ((!ec->placed) && (!ec->re_manage) &&
            (e_config->window_placement_policy == E_WINDOW_PLACEMENT_MANUAL) &&
            (!((ec->icccm.transient_for != 0) ||
               (ec->dialog))) &&
            (!ecmove) && (!ecresize))
          {
             /* Set this window into moving state */

             ec->cur_mouse_action = e_action_find("window_move");
             if (ec->next_mouse_action_ignore)
               ec->cur_mouse_action = NULL;
             if (ec->cur_mouse_action)
               {
                  if ((!ec->cur_mouse_action->func.end_mouse) &&
                      (!ec->cur_mouse_action->func.end))
                    ec->cur_mouse_action = NULL;
                  if (ec->cur_mouse_action)
                    {
                       int t;
                       ec->x = x - (ec->w >> 1);
                       e_comp_object_frame_geometry_get(ec->frame, NULL, NULL, &t, NULL);
                       ec->y = y - (t >> 1);
                       EC_CHANGED(ec);
                       ec->changes.pos = 1;
                    }
               }
          }

        evas_object_show(ec->frame);
        if (ec->cur_mouse_action)
          {
             ec->moveinfo.down.x = ec->x;
             ec->moveinfo.down.y = ec->y;
             ec->moveinfo.down.w = ec->w;
             ec->moveinfo.down.h = ec->h;
             ec->mouse.current.mx = x;
             ec->mouse.current.my = y;
             ec->moveinfo.down.button = 0;
             ec->moveinfo.down.mx = x;
             ec->moveinfo.down.my = y;

             e_object_ref(E_OBJECT(ec->cur_mouse_action));
             ec->cur_mouse_action->func.go(E_OBJECT(ec), NULL);
             if (e_config->border_raise_on_mouse_action)
               evas_object_raise(ec->frame);
             evas_object_focus_set(ec->frame, 1);
          }
        if (evas_object_visible_get(ec->frame))
          ec->changes.visible = 0;
     }
   else if ((ec->changes.visible) && (ec->new_client))
     {
        ec->changes.visible = 0;
        if (!ec->iconic)
          _e_client_event_simple(ec, E_EVENT_CLIENT_HIDE);
     }

   if (ec->changes.icon)
     {
        if (!ec->new_client)
          E_FREE_FUNC(ec->desktop, efreet_desktop_free);
        if (ec->remember && ec->remember->prop.desktop_file)
          {
             Efreet_Desktop *d;
             const char *desktop = ec->remember->prop.desktop_file;

             d = efreet_desktop_get(desktop);
             if (!d)
               d = efreet_util_desktop_name_find(desktop);
             if (d)
               {
                  efreet_desktop_free(ec->desktop);
                  ec->desktop = d;
               }
          }
        if (!ec->desktop)
          {
             if (ec->steam.steam_game_id)
               {
                  Efreet_Desktop *d;
                  Eina_List *desks = efreet_util_desktop_name_glob_list("*");
                  EINA_LIST_FREE(desks, d)
                    {
                       if (!d->exec) continue;
                       if (!strncmp(d->exec, "steam ", 6))
                         {
                            const char *st = strstr(d->exec, "steam://rungameid/");
                            if (st)
                              {
                                 st += strlen("steam://rungameid/");
                                 unsigned int id = atoi(st);
                                 if (id == ec->steam.steam_game_id)
                                   ec->desktop = d;
                              }
                         }
                    }
               }
          }
        if (!ec->desktop)
          {
             E_Exec_Instance *inst;

             inst = e_exec_startup_id_pid_instance_find(ec->netwm.startup_id,
                                                        ec->netwm.pid);
             if (inst && inst->clients)
               {
                  E_Client *ec2 = eina_list_data_get(inst->clients);

                  if (ec2->netwm.pid == ec->netwm.pid)
                    ec->desktop = inst->desktop;
               }
             else if (inst)
               ec->desktop = inst->desktop;
             if (ec->desktop) efreet_desktop_ref(ec->desktop);
          }
        if (!ec->desktop)
          {
             if (ec->internal && (ec->icccm.class && (!strncmp(ec->icccm.class, "e_fwin::", 8))))
               ec->desktop = efreet_util_desktop_exec_find("enlightenment_filemanager");
          }
        if (!ec->desktop)
          {
             if ((ec->icccm.name) || (ec->icccm.class))
               ec->desktop = efreet_util_desktop_wm_class_find(ec->icccm.name,
                                                               ec->icccm.class);
          }
        if (!ec->desktop && ec->icccm.command.argv && (ec->icccm.command.argc > 0))
          {
             ec->desktop = efreet_util_desktop_exec_find(ec->icccm.command.argv[0]);
          }
        if (!ec->desktop)
          {
             // special case hacks for specific apps that just don't do things
             // right so we have to work around them
             if (ec->icccm.class && ec->icccm.name &&
                 (!strcmp(ec->icccm.class, "Steam")) &&
                 (!strcmp(ec->icccm.name, "Steam")))
               {
                  ec->desktop = efreet_util_desktop_file_id_find("steam.desktop");
               }
             /* libreoffice and maybe others match window class
                with .desktop file name */
             else if (ec->icccm.class)
               {
                  char buf[4096] = {0};
                  snprintf(buf, sizeof(buf), "%s.desktop", ec->icccm.class);
                  ec->desktop = efreet_util_desktop_file_id_find(buf);
                  if (!ec->desktop)
                    {
                       char *s;

                       strncpy(buf, ec->icccm.class, sizeof(buf) - 1);
                       s = buf;
                       eina_str_tolower(&s);
                       if (strcmp(s, ec->icccm.class))
                         ec->desktop = efreet_util_desktop_exec_find(s);
                    }
               }
          }
        if (!ec->desktop && ec->icccm.name)
          {
             /* this works for most cases as fallback. useful when app is
                run from a shell  */
             ec->desktop = efreet_util_desktop_exec_find(ec->icccm.name);
          }
        if (!ec->desktop && ec->parent)
          {
             E_Client *ec2 = ec->parent;
             if (ec2->desktop)
               {
                  efreet_desktop_ref(ec2->desktop);
                  ec->desktop = ec2->desktop;
               }
          }

        if (ec->desktop)
          {
             if (!ec->exe_inst)
               e_exec_phony(ec);
             if (!ec->exe_inst->desktop)
               {
                  efreet_desktop_ref(ec->desktop);
                  ec->exe_inst->desktop = ec->desktop;
               }
          }
        ec->changes.icon = !e_comp_object_frame_icon_update(ec->frame);
        prop |= E_CLIENT_PROPERTY_ICON;
     }

   if (ec->new_client)
     e_comp->new_clients--;
   ec->new_client = 0;
   ec->changed = ec->changes.pos || ec->changes.size ||
                 ec->changes.stack || ec->changes.prop || ec->changes.border ||
                 ec->changes.reset_gravity || ec->changes.shading || ec->changes.shaded ||
                 ec->changes.shape || ec->changes.shape_input || ec->changes.icon ||
                 ec->changes.internal_state ||
                 ec->changes.need_maximize || ec->changes.need_unmaximize;
   ec->changes.stack = 0;

   if ((!ec->input_only) && (!ec->iconic) &&
       ((!ec->zone) || e_client_util_desk_visible(ec, e_desk_current_get(ec->zone))) &&
       ((ec->take_focus) || (ec->want_focus)))
     {
        ec->take_focus = 0;
        if ((ec->stack.prev) && (!ec->stack.next) &&
            (ec->stack.prev == e_client_focused_get()))
          {
             e_client_focus_set_with_pointer(ec);
          }
        else if ((e_config->focus_setting == E_FOCUS_NEW_WINDOW) || (ec->want_focus))
          {
             ec->want_focus = 0;
             e_client_focus_set_with_pointer(ec);
          }
        else if (ec->dialog)
          {
             if ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) ||
                 ((e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED) &&
                  (ec->parent == e_client_focused_get())))
               {
                  e_client_focus_set_with_pointer(ec);
               }
          }
        else
          {
             /* focus window by default when it is the only one on desk */
             E_Client *ec2 = NULL;
             Eina_List *l;
             EINA_LIST_FOREACH(focus_stack, l, ec2)
               {
                  if (ec == ec2) continue;
                  if ((!ec2->iconic) && (ec2->visible) &&
                      ((ec->desk == ec2->desk) || ec2->sticky))
                    break;
               }

             if (!ec2)
               {
                  e_client_focus_set_with_pointer(ec);
               }
          }
     }
   else
     ec->take_focus = ec->want_focus = 0;

   if (ec->changes.need_maximize)
     {
        E_Maximize max = ec->maximized;
        ec->maximized = E_MAXIMIZE_NONE;
        e_client_maximize(ec, max);
        ec->changes.need_maximize = 0;
     }
   else if (ec->changes.need_unmaximize)
     {
        e_client_unmaximize(ec, ec->maximized);
        ec->changes.need_unmaximize = 0;
     }

   if (ec->need_fullscreen)
     {
        e_client_fullscreen(ec, e_config->fullscreen_policy);
        ec->need_fullscreen = 0;
     }

   if (rem_change)
     e_remember_update(ec);

   if (send_event && rem_change && prop)
     {
        _e_client_event_property(ec, prop);
     }
   _e_client_hook_call(E_CLIENT_HOOK_EVAL_END, ec);
}

static void
_e_client_frame_update(E_Client *ec)
{
   const char *bordername;

   ec->border.changed = 0;
   if ((!e_comp_object_frame_allowed(ec->frame)) && (!e_comp_object_frame_exists(ec->frame)))
     return;
   if (ec->fullscreen || ec->borderless)
     bordername = "borderless";
   else if (ec->bordername)
     bordername = ec->bordername;
   else if (ec->mwm.borderless)
     bordername = "borderless";
   else if (((ec->icccm.transient_for != 0) || (ec->dialog)) &&
            (ec->icccm.min_w == ec->icccm.max_w) &&
            (ec->icccm.min_h == ec->icccm.max_h))
     bordername = "noresize_dialog";
   else if ((ec->icccm.min_w == ec->icccm.max_w) &&
            (ec->icccm.min_h == ec->icccm.max_h))
     bordername = "noresize";
   else if (ec->shaped)
     bordername = "shaped";
   else if (e_pixmap_is_x(ec->pixmap) &&
            ((!ec->icccm.accepts_focus) &&
            (!ec->icccm.take_focus)))
     bordername = "nofocus";
   else if (ec->urgent)
     bordername = "urgent";
   else if (((ec->icccm.transient_for && (!ec->netwm.type)) || (ec->dialog)) &&
            (e_pixmap_is_x(ec->pixmap)))
     bordername = "dialog";
   else if (ec->netwm.state.modal)
     bordername = "modal";
   else if ((ec->netwm.state.skip_taskbar) ||
            (ec->netwm.state.skip_pager))
     bordername = "skipped";
  /*
   else if ((ec->internal) && (ec->icccm.class) &&
            (!strncmp(ec->icccm.class, "e_fwin", 6)))
     bordername = "internal_fileman";
  */
   else
     bordername = e_config->theme_default_border_style;
   if (!bordername) bordername = "default";

   e_client_border_set(ec, bordername);
}

////////////////////////////////////////////////
EINTERN void
e_client_idler_before(void)
{
   const Eina_List *l;
   E_Client *ec;

   if ((!eina_hash_population(clients_hash[0])) && (!eina_hash_population(clients_hash[1]))) return;

   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        Eina_Stringshare *title;
        // pass 1 - eval0. fetch properties on new or on change and
        // call hooks to decide what to do - maybe move/resize
        if (ec->ignored || (!ec->changed)) continue;

        if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_FETCH, ec)) continue;
        /* FETCH is hooked by the compositor to get client hints */
        title = e_client_util_name_get(ec);
        if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_FETCH, ec)) continue;
        if (title != e_client_util_name_get(ec))
          _e_client_event_property(ec, E_CLIENT_PROPERTY_TITLE);
        /* PRE_POST_FETCH calls e_remember apply for new client */
        if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_POST_FETCH, ec)) continue;
        if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_FETCH, ec)) continue;
        if (!_e_client_hook_call(E_CLIENT_HOOK_EVAL_PRE_FRAME_ASSIGN, ec)) continue;

        if ((ec->border.changed) && (!ec->shaded) && (!e_client_is_stacking(ec)) &&
            ((!ec->override) || ec->internal) &&
            (!(((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN))))
          _e_client_frame_update(ec);
        ec->border.changed = 0;
        _e_client_hook_call(E_CLIENT_HOOK_EVAL_POST_FRAME_ASSIGN, ec);
     }

   E_CLIENT_FOREACH(ec)
     {
        if (ec->ignored) continue;
        // pass 2 - show windows needing show
        if ((ec->changes.visible) && (ec->visible) &&
            (!ec->new_client) && (!ec->changes.pos) &&
            (!ec->changes.size))
          {
             evas_object_show(ec->frame);
             ec->changes.visible = !evas_object_visible_get(ec->frame);
          }

        if ((!ec->new_client) && (!e_client_util_ignored_get(ec)) &&
            (!E_INSIDE(ec->x, ec->y, 0, 0, e_comp->w - 5, e_comp->h - 5)) &&
            (!E_INSIDE(ec->x, ec->y, 0 - ec->w + 5, 0 - ec->h + 5, e_comp->w - 5, e_comp->h - 5))
            )
          {
             if (e_config->screen_limits != E_CLIENT_OFFSCREEN_LIMIT_ALLOW_FULL)
               {
                  if (ec->parent)
                    e_comp_object_util_center_on(ec->frame, ec->parent->frame);
                  else
                    _e_client_move_lost_window_to_center(ec);
               }
          }
        // handle window stack
        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, *bottom, *moving = NULL, *rel;
                  int x, y;

                  bottom = rel = e_client_stack_bottom_get(ec);
                  EINA_LIST_FOREACH(list, ll, child)
                    {
                       if (child->moving)
                         {
                            moving = child;
                            break;
                         }
                    }
                  if (moving)
                    {
                       Evas_Coord ox, oy;

                       evas_object_geometry_get(ec->frame, &ox, &oy, NULL, NULL);
                       rel = moving;
                    }
                  EINA_LIST_FOREACH(list, ll, child)
                    {
                       if (moving)
                         {
                            if (child == moving) continue;
                         }
                       else if (child == bottom) continue;
                       x = rel->x + ((rel->w - child->w) / 2);
                       y = rel->y + ((rel->h - child->h) / 2);
                       if ((x != child->x)  || (y != child->y))
                         {
                            child->x = x;
                            child->y = y;
                            child->pre_cb.x = x;
                            child->pre_cb.y = y;
                            child->changes.pos = 1;
                            child->changed = 1;
                         }
                    }
                  e_client_stack_list_finish(list);
               }
          }
     }

   if (_e_client_layout_cb)
     _e_client_layout_cb();

   // pass 3 - hide windows needing hide and eval (main eval)
   E_CLIENT_FOREACH(ec)
     {
        if (ec->ignored || e_object_is_del(E_OBJECT(ec))) continue;

        if ((ec->changes.visible) && (!ec->visible))
          {
             evas_object_hide(ec->frame);
             ec->changes.visible = 0;
          }

        if (ec->changed)
          _e_client_eval(ec);

        if ((ec->changes.visible) && (ec->visible) && (!ec->changed))
          {
             evas_object_show(ec->frame);
             ec->changes.visible = !evas_object_visible_get(ec->frame);
             if (ec->changes.visible) EC_CHANGED(ec);
             if (!e_client_util_desk_visible(ec, e_desk_current_get(ec->zone)))
               evas_object_hide(ec->frame);
          }
     }
}


EINTERN Eina_Bool
e_client_init(void)
{
   clients_hash[0] = eina_hash_pointer_new(NULL);
   clients_hash[1] = eina_hash_pointer_new(NULL);

   E_LIST_HANDLER_APPEND(handlers, E_EVENT_POINTER_WARP,
                         _e_client_cb_pointer_warp, NULL);
   E_LIST_HANDLER_APPEND(handlers, EFREET_EVENT_DESKTOP_CACHE_UPDATE,
                         _e_client_cb_efreet_cache_update, NULL);
   E_LIST_HANDLER_APPEND(handlers, EFREET_EVENT_ICON_CACHE_UPDATE,
                         _e_client_cb_efreet_cache_update, NULL);
   E_LIST_HANDLER_APPEND(handlers, E_EVENT_CONFIG_ICON_THEME,
                         _e_client_cb_config_icon_theme, NULL);
   E_LIST_HANDLER_APPEND(handlers, E_EVENT_CONFIG_MODE_CHANGED,
                         _e_client_cb_config_mode, NULL);
   E_LIST_HANDLER_APPEND(handlers, E_EVENT_DESK_WINDOW_PROFILE_CHANGE,
                         _e_client_cb_desk_window_profile_change, NULL);

   E_EVENT_CLIENT_ADD = ecore_event_type_new();
   E_EVENT_CLIENT_REMOVE = ecore_event_type_new();
   E_EVENT_CLIENT_DESK_SET = ecore_event_type_new();
   E_EVENT_CLIENT_ZONE_SET = ecore_event_type_new();
   E_EVENT_CLIENT_RESIZE = ecore_event_type_new();
   E_EVENT_CLIENT_MOVE = ecore_event_type_new();
   E_EVENT_CLIENT_SHOW = ecore_event_type_new();
   E_EVENT_CLIENT_HIDE = ecore_event_type_new();
   E_EVENT_CLIENT_ICONIFY = ecore_event_type_new();
   E_EVENT_CLIENT_UNICONIFY = ecore_event_type_new();
   E_EVENT_CLIENT_STACK = ecore_event_type_new();
   E_EVENT_CLIENT_FOCUS_IN = ecore_event_type_new();
   E_EVENT_CLIENT_FOCUS_OUT = ecore_event_type_new();
   E_EVENT_CLIENT_PROPERTY = ecore_event_type_new();
   E_EVENT_CLIENT_FULLSCREEN = ecore_event_type_new();
   E_EVENT_CLIENT_UNFULLSCREEN = ecore_event_type_new();

   return (!!clients_hash[1]);
}

EINTERN void
e_client_shutdown(void)
{
   E_FREE_FUNC(clients_hash[0], eina_hash_free);
   E_FREE_FUNC(clients_hash[1], eina_hash_free);

   E_FREE_LIST(handlers, ecore_event_handler_del);

   e_int_client_menu_hooks_clear();
   E_FREE_FUNC(warp_timer, ecore_timer_del);
   warp_client = NULL;
}

E_API void
e_client_unignore(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->ignored) return;

   ec->ignored = 0;
   if (!e_client_util_ignored_get(ec))
     {
        if (starting)
          focus_stack = eina_list_prepend(focus_stack, ec);
        else
          focus_stack = eina_list_append(focus_stack, ec);
     }
   _e_client_event_simple(ec, E_EVENT_CLIENT_ADD);
   _e_client_hook_call(E_CLIENT_HOOK_UNIGNORE, ec);
}

E_API E_Client *
e_client_new(E_Pixmap *cp, int first_map, int internal)
{
   E_Client *ec;
   E_Pixmap_Type ptype = e_pixmap_type_get(cp);

   if ((ptype != E_PIXMAP_TYPE_X) && (ptype != E_PIXMAP_TYPE_WL)) return NULL;
   if (eina_hash_find(clients_hash[ptype], &cp)) return NULL;

   ec = E_OBJECT_ALLOC(E_Client, E_CLIENT_TYPE, _e_client_free);
   if (!ec) return NULL;
   e_object_del_func_set(E_OBJECT(ec), E_OBJECT_CLEANUP_FUNC(_e_client_del));

   ec->focus_policy_override = E_FOCUS_LAST;
   ec->w = 1;
   ec->h = 1;
   ec->internal = internal;

   ec->pixmap = cp;
   e_pixmap_client_set(cp, ec);
   ec->resize_mode = E_POINTER_RESIZE_NONE;
   ec->layer = E_LAYER_CLIENT_NORMAL;

   /* printf("##- ON MAP CLIENT 0x%x SIZE %ix%i %i:%i\n",
    *     ec->win, ec->w, ec->h, att->x, att->y); */

   /* FIXME: if first_map is 1 then we should ignore the first hide event
    * or ensure the window is already hidden and events flushed before we
    * create a border for it */
   if (first_map)
     {
        // printf("##- FIRST MAP\n");
        ec->re_manage = 1;
        // needed to be 1 for internal windw and on restart.
        // ec->ignore_first_unmap = 2;
     }
   ec->offer_resistance = 1;
   ec->new_client = 1;
   e_comp->new_clients++;

   if (!_e_client_hook_call(E_CLIENT_HOOK_NEW_CLIENT, ec))
     {
        /* delete the above allocated object */
        //e_object_del(E_OBJECT(ec));
        return NULL;
     }

   ec->icccm.title = NULL;
   ec->icccm.name = NULL;
   ec->icccm.class = NULL;
   ec->icccm.icon_name = NULL;
   ec->icccm.machine = NULL;
   ec->icccm.min_w = 1;
   ec->icccm.min_h = 1;
   ec->icccm.max_w = 32767;
   ec->icccm.max_h = 32767;
   ec->icccm.base_w = 0;
   ec->icccm.base_h = 0;
   ec->icccm.step_w = -1;
   ec->icccm.step_h = -1;
   ec->icccm.min_aspect = 0.0;
   ec->icccm.max_aspect = 0.0;

   ec->netwm.pid = 0;
   ec->netwm.name = NULL;
   ec->netwm.icon_name = NULL;
   ec->netwm.desktop = 0;
   ec->netwm.state.modal = 0;
   ec->netwm.state.sticky = 0;
   ec->netwm.state.shaded = 0;
   ec->netwm.state.hidden = 0;
   ec->netwm.state.maximized_v = 0;
   ec->netwm.state.maximized_h = 0;
   ec->netwm.state.skip_taskbar = 0;
   ec->netwm.state.skip_pager = 0;
   ec->netwm.state.fullscreen = 0;
   ec->netwm.state.stacking = E_STACKING_NONE;
   ec->netwm.action.move = 0;
   ec->netwm.action.resize = 0;
   ec->netwm.action.minimize = 0;
   ec->netwm.action.shade = 0;
   ec->netwm.action.stick = 0;
   ec->netwm.action.maximized_h = 0;
   ec->netwm.action.maximized_v = 0;
   ec->netwm.action.fullscreen = 0;
   ec->netwm.action.change_desktop = 0;
   ec->netwm.action.close = 0;
   ec->netwm.opacity = 255;

   e_comp->clients = eina_list_append(e_comp->clients, ec);
   eina_hash_add(clients_hash[ptype], &ec->pixmap, ec);

   if (!ec->ignored) EC_CHANGED(ec);

   e_comp_object_client_add(ec);

   if (ec->override)
     _e_client_zone_update(ec);
   else if (!ec->desk)
     e_client_desk_set(ec, e_desk_current_get(e_zone_current_get()));
   if (!ec->re_manage)
     ec->placed = ec->changes.pos = 0; //ensure placement is run
   if (ec->frame)
     {
        evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_SHOW, _e_client_cb_evas_show, ec);
        evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_HIDE, _e_client_cb_evas_hide, ec);
        evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_MOVE, _e_client_cb_evas_move, ec);
        evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_RESIZE, _e_client_cb_evas_resize, ec);
        evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_RESTACK, _e_client_cb_evas_restack, ec);
        evas_object_smart_callback_add(ec->frame, "shade_done", _e_client_cb_evas_shade_done, ec);
        if (ec->override)
          evas_object_layer_set(ec->frame, E_LAYER_CLIENT_ABOVE);
        else
          evas_object_layer_set(ec->frame, E_LAYER_CLIENT_NORMAL);
     }
   if (!e_client_util_ignored_get(ec))
     {
        if (starting)
          focus_stack = eina_list_prepend(focus_stack, ec);
        else
          focus_stack = eina_list_append(focus_stack, ec);
     }

   e_hints_client_list_set();
   return ec;
}

E_API Eina_Bool
e_client_desk_window_profile_available_check(E_Client *ec, const char *profile)
{
   int i;

   E_OBJECT_CHECK_RETURN(ec, EINA_FALSE);
   E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, EINA_FALSE);
   EINA_SAFETY_ON_NULL_RETURN_VAL(profile, EINA_FALSE);
   EINA_SAFETY_ON_FALSE_RETURN_VAL(ec->e.state.profile.use, EINA_FALSE);
   if (ec->e.state.profile.num == 0) return EINA_TRUE;

   for (i = 0; i < ec->e.state.profile.num; i++)
     {
        if (!e_util_strcmp(ec->e.state.profile.available_list[i],
                           profile))
          return EINA_TRUE;
     }

   return EINA_FALSE;
}

E_API void
e_client_desk_window_profile_wait_desk_set(E_Client *ec, E_Desk *desk)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   E_OBJECT_CHECK(desk);
   E_OBJECT_TYPE_CHECK(desk, E_DESK_TYPE);

   if (ec->e.state.profile.wait_desk == desk) return;

   if (ec->e.state.profile.wait_desk_delfn)
     {
        if (ec->e.state.profile.wait_desk)
          e_object_delfn_del(E_OBJECT(ec->e.state.profile.wait_desk),
                             ec->e.state.profile.wait_desk_delfn);
        ec->e.state.profile.wait_desk_delfn = NULL;
     }

   if (ec->e.state.profile.wait_desk)
     e_object_unref(E_OBJECT(ec->e.state.profile.wait_desk));
   ec->e.state.profile.wait_desk = NULL;

   if (desk)
     {
        ec->e.state.profile.wait_desk_delfn =
           e_object_delfn_add(E_OBJECT(desk),
                              _e_client_desk_window_profile_wait_desk_delfn,
                              ec);
     }
   ec->e.state.profile.wait_desk = desk;
   if (ec->e.state.profile.wait_desk)
     e_object_ref(E_OBJECT(ec->e.state.profile.wait_desk));
}

E_API void
e_client_desk_set(E_Client *ec, E_Desk *desk)
{
   E_Event_Client_Desk_Set *ev;
   E_Desk *old_desk;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   E_OBJECT_CHECK(desk);
   E_OBJECT_TYPE_CHECK(desk, E_DESK_TYPE);
   if (ec->desk == desk) return;
   if (ec->e.state.profile.use)
     {
        const char *profile = desk->window_profile;

        if (!profile) profile = elm_config_profile_get();
        if (!profile) profile = "standard";
        if (e_util_strcmp(ec->e.state.profile.name, profile))
          {
             if (e_client_desk_window_profile_available_check(ec, profile))
               {
                  eina_stringshare_replace(&ec->e.state.profile.set, profile);
                  eina_stringshare_replace(&ec->e.state.profile.wait, NULL);
                  ec->e.state.profile.wait_for_done = 0;
                  e_client_desk_window_profile_wait_desk_set(ec, desk);
                  EC_CHANGED(ec);
               }
          }
     }

   if (ec->fullscreen)
     {
        if (ec->desk)
          ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec);
        desk->fullscreen_clients = eina_list_append(desk->fullscreen_clients, ec);
     }
   old_desk = ec->desk;
   ec->desk = desk;
   if (ec->frame)
     {
        e_comp_object_effect_unclip(ec->frame);
        e_comp_object_effect_set(ec->frame, NULL);
     }
   if (desk->visible || ec->sticky)
     {
        // force visibility if its a stack window going onto this desktop
        if (ec->stack.prev || ec->stack.next) ec->hidden = 0;
        if ((!ec->hidden) && (!ec->iconic))
          evas_object_show(ec->frame);
     }
   else
     {
        ec->hidden = 1;
        evas_object_hide(ec->frame);
     }
   e_client_comp_hidden_set(ec, (!desk->visible) && (!ec->sticky));
   e_client_zone_set(ec, desk->zone);

   e_hints_window_desktop_set(ec);

   if (old_desk)
     {
        ev = E_NEW(E_Event_Client_Desk_Set, 1);
        ev->ec = ec;
        UNREFD(ec, 4);
        e_object_ref(E_OBJECT(ec));
        ev->desk = old_desk;
        e_object_ref(E_OBJECT(old_desk));
        ecore_event_add(E_EVENT_CLIENT_DESK_SET, ev, (Ecore_End_Cb)_e_client_event_desk_set_free, NULL);

        if (old_desk->zone == ec->zone)
          {
             if (ec->maximized || ec->fullscreen)
               e_client_rescale(ec);
          }
     }

   if (ec->stack.prev || ec->stack.next)
     {
        if (ec->stack.ignore == 0)
          {
             Eina_List *l, *list = e_client_stack_list_prepare(ec);
             E_Client *child;

             EINA_LIST_FOREACH(list, l, child)
               {
                  if (child == ec) break;
                  e_client_desk_set(child, ec->desk);
                  evas_object_stack_below(child->frame, ec->frame);
               }
             EINA_LIST_REVERSE_FOREACH(list, l, child)
               {
                  if (child == ec) break;
                  e_client_desk_set(child, ec->desk);
                  evas_object_stack_above(child->frame, ec->frame);
               }
             e_client_stack_list_finish(list);
          }
     }
   else
     {
        if (e_config->transient.desktop)
          {
             E_Client *child;
             const Eina_List *l;

             EINA_LIST_FOREACH(ec->transients, l, child)
               e_client_desk_set(child, ec->desk);
             e_client_transients_restack(ec);
          }
     }

   e_remember_update(ec);
   _e_client_hook_call(E_CLIENT_HOOK_DESK_SET, ec);
   evas_object_smart_callback_call(ec->frame, "desk_change", ec);
}

E_API Eina_Bool
e_client_comp_grabbed_get(void)
{
   return comp_grabbed;
}

E_API E_Client *
e_client_action_get(void)
{
   return action_client;
}

E_API E_Client *
e_client_warping_get(void)
{
   return warp_client;
}


E_API Eina_List *
e_clients_immortal_list(void)
{
   const Eina_List *l;
   Eina_List *list = NULL;
   E_Client *ec;

   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        if (ec->lock_life)
          list = eina_list_append(list, ec);
     }
   return list;
}

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

E_API void
e_client_mouse_in(E_Client *ec, int x, int y)
{
   if (comp_grabbed) return;
   if (warp_client && (ec != warp_client)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (ec->desk && ec->desk->animate_count) return;
   ec->mouse.current.mx = x;
   ec->mouse.current.my = y;
   ec->mouse.in = 1;
   if ((!ec->iconic) && (!e_client_util_ignored_get(ec)))
     e_focus_event_mouse_in(ec);
}

E_API void
e_client_mouse_out(E_Client *ec, int x, int y)
{
   if (comp_grabbed) return;
   if (ec->desk && ec->desk->animate_count) return;
   if (e_pixmap_is_x(ec->pixmap) && E_INSIDE(x, y, ec->x, ec->y, ec->w, ec->h)) return;

   ec->mouse.current.mx = x;
   ec->mouse.current.my = y;
   ec->mouse.in = 0;
   if (ec->fullscreen) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if ((!ec->iconic) && (!e_client_util_ignored_get(ec)))
     e_focus_event_mouse_out(ec);
}

E_API void
e_client_mouse_wheel(E_Client *ec, Evas_Point *output, E_Binding_Event_Wheel *ev)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (action_client) return;
   ec->mouse.current.mx = output->x;
   ec->mouse.current.my = output->y;
   if ((!ec->cur_mouse_action) && (!e_client_util_ignored_get(ec)))
     e_bindings_wheel_event_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(ec), ev);
}

E_API void
e_client_mouse_down(E_Client *ec, int button, Evas_Point *output, E_Binding_Event_Mouse_Button *ev)
{
   Eina_Bool did_act = EINA_FALSE;
   E_Client *pfocus;
   int player;

   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (action_client || ec->iconic || e_client_util_ignored_get(ec)) return;
   if ((button >= 1) && (button <= 3))
     {
        ec->mouse.last_down[button - 1].mx = output->x;
        ec->mouse.last_down[button - 1].my = output->y;
        ec->mouse.last_down[button - 1].x = ec->x;
        ec->mouse.last_down[button - 1].y = ec->y;
        ec->mouse.last_down[button - 1].w = ec->w;
        ec->mouse.last_down[button - 1].h = ec->h;
     }
   else
     {
        ec->moveinfo.down.x = ec->x;
        ec->moveinfo.down.y = ec->y;
        ec->moveinfo.down.w = ec->w;
        ec->moveinfo.down.h = ec->h;
     }
   ec->mouse.current.mx = output->x;
   ec->mouse.current.my = output->y;
   pfocus = e_client_focused_get();
   player = ec->layer;
   if (!ec->cur_mouse_action)
     {
        ec->cur_mouse_action =
          e_bindings_mouse_down_event_handle(E_BINDING_CONTEXT_WINDOW,
                                             E_OBJECT(ec), ev);
        if (ec->next_mouse_action_ignore)
          ec->cur_mouse_action = NULL;
        if (ec->cur_mouse_action)
          {
             did_act = EINA_TRUE;
             e_object_ref(E_OBJECT(ec->cur_mouse_action));
             if (ec->internal)
               {
                  int button_mask, i;
                  Evas *e;

                  e = evas_object_evas_get(ec->internal_elm_win);
                  button_mask = evas_pointer_button_down_mask_get(e);
                  for (i = 0; i < 32; i++)
                    {
                      if ((button_mask & (1 << i)))
                        evas_event_feed_mouse_up(e, i + 1, EVAS_BUTTON_NONE, 0, NULL);
                    }
                  evas_event_feed_mouse_out(e, 0, NULL);
               }
          }
     }
   if ((!did_act) || (((pfocus == e_client_focused_get()) || (ec == e_client_focused_get())) && (ec->layer >= player)))
     e_focus_event_mouse_down(ec);
   if ((button >= 1) && (button <= 3))
     {
        ec->mouse.last_down[button - 1].mx = output->x;
        ec->mouse.last_down[button - 1].my = output->y;
        ec->mouse.last_down[button - 1].x = ec->x;
        ec->mouse.last_down[button - 1].y = ec->y;
        ec->mouse.last_down[button - 1].w = ec->w;
        ec->mouse.last_down[button - 1].h = ec->h;
     }
   else
     {
        ec->moveinfo.down.x = ec->x;
        ec->moveinfo.down.y = ec->y;
        ec->moveinfo.down.w = ec->w;
        ec->moveinfo.down.h = ec->h;
     }
   ec->mouse.current.mx = output->x;
   ec->mouse.current.my = output->y;
}

E_API void
e_client_mouse_up(E_Client *ec, int button, Evas_Point *output, E_Binding_Event_Mouse_Button* ev)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (ec->iconic || e_client_util_ignored_get(ec)) return;
   if ((button >= 1) && (button <= 3))
     {
        ec->mouse.last_up[button - 1].mx = output->x;
        ec->mouse.last_up[button - 1].my = output->y;
        ec->mouse.last_up[button - 1].x = ec->x;
        ec->mouse.last_up[button - 1].y = ec->y;
     }
   ec->mouse.current.mx = output->x;
   ec->mouse.current.my = output->y;
   /* also we don't pass the same params that went in - then again that */
   /* should be ok as we are just ending the action if it has an end */
   if (ec->cur_mouse_action)
     _e_client_mouse_action_end(ec);
   else
     {
        if (!e_bindings_mouse_up_event_handle(E_BINDING_CONTEXT_WINDOW, E_OBJECT(ec), ev))
          e_focus_event_mouse_up(ec);
     }
   if ((button >= 1) && (button <= 3))
     {
        ec->mouse.last_up[button - 1].mx = output->x;
        ec->mouse.last_up[button - 1].my = output->y;
        ec->mouse.last_up[button - 1].x = ec->x;
        ec->mouse.last_up[button - 1].y = ec->y;
     }

   ec->drag.start = 0;
}

E_API void
e_client_mouse_move(E_Client *ec, Evas_Point *output)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (ec->iconic || e_client_util_ignored_get(ec)) return;
   if ((ec->mouse.current.mx == output->x) && (ec->mouse.current.my == output->y)) return;
   ec->mouse.current.mx = output->x;
   ec->mouse.current.my = output->y;
   if (ec->moving)
     {
        int x, y, new_x, new_y;
        int new_w, new_h;
        Eina_List *skiplist = NULL;

        if (action_handler_key) return;
        if ((ec->moveinfo.down.button >= 1) && (ec->moveinfo.down.button <= 3))
          {
             x = ec->mouse.last_down[ec->moveinfo.down.button - 1].x +
               (ec->mouse.current.mx - ec->moveinfo.down.mx);
             y = ec->mouse.last_down[ec->moveinfo.down.button - 1].y +
               (ec->mouse.current.my - ec->moveinfo.down.my);
          }
        else
          {
             x = ec->moveinfo.down.x +
               (ec->mouse.current.mx - ec->moveinfo.down.mx);
             y = ec->moveinfo.down.y +
               (ec->mouse.current.my - ec->moveinfo.down.my);
          }
        e_comp_object_frame_xy_adjust(ec->frame, x, y, &new_x, &new_y);

        skiplist = eina_list_append(skiplist, ec);
        e_resist_client_position(skiplist,
                                 ec->x, ec->y, ec->w, ec->h,
                                 x, y, ec->w, ec->h,
                                 &new_x, &new_y, &new_w, &new_h);
        eina_list_free(skiplist);

        if (e_config->screen_limits == E_CLIENT_OFFSCREEN_LIMIT_ALLOW_NONE)
          _e_client_stay_within_canvas(ec, x, y, &new_x, &new_y);

        ec->shelf_fix.x = 0;
        ec->shelf_fix.y = 0;
        ec->shelf_fix.modified = 0;
        evas_object_move(ec->frame, new_x, new_y);
     }
   else if (e_client_util_resizing_get(ec))
     {
        if (action_handler_key) return;
        _e_client_resize_handle(ec);
     }
   else if (ec->drag.start)
     {
        if ((ec->drag.x == -1) && (ec->drag.y == -1))
          {
             ec->drag.x = output->x;
             ec->drag.y = output->y;
          }
        else if (ec->zone)
          {
             int dx, dy;

             dx = ec->drag.x - output->x;
             dy = ec->drag.y - output->y;
             if (((dx * dx) + (dy * dy)) >
                 (e_config->drag_resist * e_config->drag_resist))
               {
                  /* start drag! */
                  if (
#ifndef HAVE_WAYLAND_ONLY
                    ec->netwm.icons ||
#endif
                    ec->desktop || ec->internal_icon)
                    {
                       Evas_Object *o = NULL;
                       int x, y, w, h;
                       const char *drag_types[] = { "enlightenment/border" };

                       REFD(ec, 1);
                       e_object_ref(E_OBJECT(ec));
                       e_comp_object_frame_icon_geometry_get(ec->frame, &x, &y, &w, &h);

                       client_drag = e_drag_new(output->x, output->y,
                                                drag_types, 1, ec, -1,
                                                NULL,
                                                _e_client_cb_drag_finished);
                       client_drag->button_mask = evas_pointer_button_down_mask_get(e_comp->evas);
                       e_drag_resize(client_drag, w, h);

                       o = e_client_icon_add(ec, client_drag->evas);
                       if (!o)
                         {
                            /* FIXME: fallback icon for drag */
                            o = evas_object_rectangle_add(client_drag->evas);
                            evas_object_color_set(o, 255, 255, 255, 255);
                         }

                       e_drag_object_set(client_drag, o);
                       e_drag_start(client_drag,
                                    output->x + (ec->drag.x - x),
                                    output->y + (ec->drag.y - y));
                    }
                  ec->drag.start = 0;
               }
          }
     }
}
///////////////////////////////////////////////////////

E_API void
e_client_res_change_geometry_save(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (ec->pre_res_change.valid) return;
   ec->pre_res_change.valid = 1;
   ec->pre_res_change.x = ec->x;
   ec->pre_res_change.y = ec->y;
   ec->pre_res_change.w = ec->w;
   ec->pre_res_change.h = ec->h;
   ec->pre_res_change.saved.x = ec->saved.x;
   ec->pre_res_change.saved.y = ec->saved.y;
   ec->pre_res_change.saved.w = ec->saved.w;
   ec->pre_res_change.saved.h = ec->saved.h;
}

E_API void
e_client_res_change_geometry_restore(E_Client *ec)
{
   struct
   {
      unsigned char valid E_BITFIELD;
      int           x, y, w, h;
      struct
      {
         int x, y, w, h;
      } saved;
   } pre_res_change;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->pre_res_change.valid) return;
   if (ec->new_client) return;
   if (!ec->zone) return;

   memcpy(&pre_res_change, &ec->pre_res_change, sizeof(pre_res_change));

   if (ec->fullscreen)
     {
        if ((eina_list_count(e_comp->zones) > 1) ||
            (e_config->fullscreen_policy == E_FULLSCREEN_RESIZE))
          evas_object_geometry_set(ec->frame, ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h);
        else
          {
             e_client_unfullscreen(ec);
             e_client_fullscreen(ec, e_config->fullscreen_policy);
          }
     }
   else if (ec->maximized != E_MAXIMIZE_NONE)
     {
        int x, y, w, h;

        if (e_client_maximize_geometry_get(ec, ec->maximized, &x, &y, &w, &h))
          {
             Eina_Bool override = ec->maximize_override;
             ec->maximize_override = 1;
             evas_object_geometry_set(ec->frame, x, y, w, h);
             ec->maximize_override = override;
          }
        else
          {
             E_Maximize max = ec->maximized;
             e_client_unmaximize(ec, E_MAXIMIZE_BOTH);
             e_client_maximize(ec, max);
          }
     }
   else
     {
        int x, y, w, h, zx, zy, zw, zh;

        ec->saved.x = ec->pre_res_change.saved.x;
        ec->saved.y = ec->pre_res_change.saved.y;
        ec->saved.w = ec->pre_res_change.saved.w;
        ec->saved.h = ec->pre_res_change.saved.h;

        e_zone_useful_geometry_get(ec->zone, &zx, &zy, &zw, &zh);

        if (ec->saved.w > zw)
          ec->saved.w = zw;
        if ((ec->saved.x + ec->saved.w) > (zx + zw))
          ec->saved.x = zx + zw - ec->saved.w;

        if (ec->saved.h > zh)
          ec->saved.h = zh;
        if ((ec->saved.y + ec->saved.h) > (zy + zh))
          ec->saved.y = zy + zh - ec->saved.h;

        x = ec->pre_res_change.x;
        y = ec->pre_res_change.y;
        w = ec->pre_res_change.w;
        h = ec->pre_res_change.h;
        if (w > zw)
          w = zw;
        if (h > zh)
          h = zh;
        if ((x + w) > (zx + zw))
          x = zx + zw - w;
        if ((y + h) > (zy + zh))
          y = zy + zh - h;
        evas_object_move(ec->frame, x, y);
        if (w && h)
          {
             e_client_resize_limit(ec, &w, &h);
             evas_object_resize(ec->frame, w, h);
          }
     }
   memcpy(&ec->pre_res_change, &pre_res_change, sizeof(pre_res_change));
}

E_API void
e_client_rescale(E_Client *ec)
{
   Eina_Bool shaded;
   int shade_dir;

   if (stopping) return;
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (e_comp->updating)
     {
        ec->changes.need_rescale = 1;
        EC_CHANGED(ec);
        return;
     }
   ec->changes.need_rescale = 0;

   shaded = ec->shaded;
   shade_dir = ec->shade_dir;
   if (shaded) e_client_unshade(ec, shade_dir);
   ec->pre_res_change.valid = 0;
   e_client_res_change_geometry_save(ec);
   e_client_res_change_geometry_restore(ec);
   if (shaded) e_client_shade(ec, shade_dir);
}

E_API void
e_client_zone_set(E_Client *ec, E_Zone *zone)
{
   E_Event_Client_Zone_Set *ev;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   E_OBJECT_CHECK(zone);
   E_OBJECT_TYPE_CHECK(zone, E_ZONE_TYPE);
   if (ec->zone == zone) return;

   /* if the window does not lie in the new zone, move it so that it does */
   if (!E_INTERSECTS(ec->x, ec->y, ec->w, ec->h, zone->x, zone->y, zone->w, zone->h))
     {
        int x, y;

        if (ec->zone)
          {
             /* first guess -- get offset from old zone, and apply to new zone */
             x = zone->x + (ec->x - ec->zone->x);
             y = zone->y + (ec->y - ec->zone->y);
          }
        else
          x = ec->x, y = ec->y;

        /* keep window from hanging off bottom and left */
        if (x + ec->w > zone->x + zone->w) x += (zone->x + zone->w) - (x + ec->w);
        if (y + ec->h > zone->y + zone->h) y += (zone->y + zone->h) - (y + ec->h);

        /* make sure to and left are on screen (if the window is larger than the zone, it will hang off the bottom / right) */
        if (x < zone->x) x = zone->x;
        if (y < zone->y) y = zone->y;

        if (!E_INTERSECTS(x, y, ec->w, ec->h, zone->x, zone->y, zone->w, zone->h))
          {
             /* still not in zone at all, so just move it to closest edge */
             if (x < zone->x) x = zone->x;
             if (x >= zone->x + zone->w) x = zone->x + zone->w - ec->w;
             if (y < zone->y) y = zone->y;
             if (y >= zone->y + zone->h) y = zone->y + zone->h - ec->h;
          }
        evas_object_move(ec->frame, x, y);
     }

   ec->zone = zone;

   if ((!ec->desk) || (ec->desk->zone != ec->zone))
     e_client_desk_set(ec, e_desk_current_get(ec->zone));

   ev = E_NEW(E_Event_Client_Zone_Set, 1);
   ev->ec = ec;
   REFD(ec, 5);
   e_object_ref(E_OBJECT(ec));
   ev->zone = zone;
   e_object_ref(E_OBJECT(zone));

   ecore_event_add(E_EVENT_CLIENT_ZONE_SET, ev, (Ecore_End_Cb)_e_client_event_zone_set_free, NULL);

   e_remember_update(ec);
   if (ec->maximized || ec->fullscreen)
     e_client_rescale(ec);
}

E_API void
e_client_geometry_get(E_Client *ec, int *x, int *y, int *w, int *h)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (ec->frame)
     evas_object_geometry_get(ec->frame, x, y, w, h);
   else
     {
        if (x) *x = ec->x;
        if (y) *y = ec->y;
        if (w) *w = ec->w;
        if (h) *h = ec->h;
     }
}

E_API E_Client *
e_client_above_get(const E_Client *ec)
{
   unsigned int x;
   E_Client *ec2;

   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
   if (EINA_INLIST_GET(ec)->next) //check current layer
     {
        EINA_INLIST_FOREACH(EINA_INLIST_GET(ec)->next, ec2)
          if (!e_object_is_del(E_OBJECT(ec2)))
            return ec2;
     }
   if (ec->layer == E_LAYER_CLIENT_PRIO) return NULL;
   if (e_comp_canvas_client_layer_map(ec->layer) == 9999) return NULL;

   /* go up the layers until we find one */
   for (x = e_comp_canvas_layer_map(ec->layer) + 1; x <= e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x++)
     {
        if (!e_comp->layers[x].clients) continue;
        EINA_INLIST_FOREACH(e_comp->layers[x].clients, ec2)
          if (!e_object_is_del(E_OBJECT(ec2)))
            return ec2;
     }
   return NULL;
}

E_API E_Client *
e_client_below_get(const E_Client *ec)
{
   unsigned int x;
   E_Client *ec2;
   Eina_Inlist *l;

   E_OBJECT_CHECK_RETURN(ec, NULL);
   E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, NULL);

   EINA_SAFETY_ON_NULL_RETURN_VAL(ec, NULL);
   if (EINA_INLIST_GET(ec)->prev) //check current layer
     {
        for (l = EINA_INLIST_GET(ec)->prev; l; l = l->prev)
          {
             ec2 = EINA_INLIST_CONTAINER_GET(l, E_Client);;
             if (!e_object_is_del(E_OBJECT(ec2)))
               return ec2;
          }
     }
   if (ec->layer == E_LAYER_CLIENT_DESKTOP) return NULL;
   if (e_comp_canvas_client_layer_map(ec->layer) == 9999) return NULL;

   /* go down the layers until we find one */
   x = e_comp_canvas_layer_map(ec->layer);
   if (x > 0) x--;

   for (; x >= e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x--)
     {
        if (!e_comp->layers[x].clients) continue;
        EINA_INLIST_REVERSE_FOREACH(e_comp->layers[x].clients, ec2)
          if (!e_object_is_del(E_OBJECT(ec2)))
            return ec2;
     }
   return NULL;
}

E_API E_Client *
e_client_bottom_get(void)
{
   unsigned int x;

   for (x = e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x <= e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x++)
     {
        E_Client *ec2;

        if (!e_comp->layers[x].clients) continue;
        EINA_INLIST_FOREACH(e_comp->layers[x].clients, ec2)
          if (!e_object_is_del(E_OBJECT(ec2)))
            return ec2;
     }
   return NULL;
}

E_API E_Client *
e_client_top_get(void)
{
   unsigned int x;

   for (x = e_comp_canvas_layer_map(E_LAYER_CLIENT_PRIO); x >= e_comp_canvas_layer_map(E_LAYER_CLIENT_DESKTOP); x--)
     {
        E_Client *ec2;

        if (!e_comp->layers[x].clients) continue;
        EINA_INLIST_REVERSE_FOREACH(e_comp->layers[x].clients, ec2)
          if (!e_object_is_del(E_OBJECT(ec2)))
            return ec2;
     }
   return NULL;
}

E_API unsigned int
e_clients_count(void)
{
   return eina_list_count(e_comp->clients);
}


/**
 * Set a callback which will be called just prior to updating the
 * move coordinates for a border
 */
E_API void
e_client_move_intercept_cb_set(E_Client *ec, E_Client_Move_Intercept_Cb cb)
{
   ec->move_intercept_cb = cb;
}

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

E_API E_Client_Hook *
e_client_hook_add(E_Client_Hook_Point hookpoint, E_Client_Hook_Cb func, const void *data)
{
   E_Client_Hook *ch;

   EINA_SAFETY_ON_TRUE_RETURN_VAL(hookpoint >= E_CLIENT_HOOK_LAST, NULL);
   ch = E_NEW(E_Client_Hook, 1);
   if (!ch) return NULL;
   ch->hookpoint = hookpoint;
   ch->func = func;
   ch->data = (void*)data;
   _e_client_hooks[hookpoint] = eina_inlist_append(_e_client_hooks[hookpoint], EINA_INLIST_GET(ch));
   return ch;
}

E_API void
e_client_hook_del(E_Client_Hook *ch)
{
   ch->delete_me = 1;
   if (_e_client_hooks_walking == 0)
     {
        _e_client_hooks[ch->hookpoint] = eina_inlist_remove(_e_client_hooks[ch->hookpoint], EINA_INLIST_GET(ch));
        free(ch);
     }
   else
     _e_client_hooks_delete++;
}

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

E_API void
e_client_focus_latest_set(E_Client *ec)
{
   if (!ec) CRI("ACK");
   if (focus_track_frozen > 0) return;
   focus_stack = eina_list_remove(focus_stack, ec);
   focus_stack = eina_list_prepend(focus_stack, ec);
}

E_API void
e_client_raise_latest_set(E_Client *ec)
{
   if (!ec) CRI("ACK");
   raise_stack = eina_list_remove(raise_stack, ec);
   raise_stack = eina_list_prepend(raise_stack, ec);
}

E_API Eina_Bool
e_client_focus_track_enabled(void)
{
   return !focus_track_frozen;
}

E_API void
e_client_focus_track_freeze(void)
{
   focus_track_frozen++;
}

E_API void
e_client_focus_track_thaw(void)
{
   if (focus_track_frozen)
     focus_track_frozen--;
}

E_API void
e_client_refocus(void)
{
   E_Client *ec;
   const Eina_List *l;

   EINA_LIST_FOREACH(e_client_focus_stack_get(), l, ec)
     if (ec->desk && ec->desk->visible && (!ec->iconic))
       {
          if (e_comp->input_key_grabs || e_comp->input_mouse_grabs) break;
          evas_object_focus_set(ec->frame, 1);
          break;
       }
}


/*
 * Sets the focus to the given client if necessary
 * There are 3 cases of different focus_policy-configurations:
 *
 * - E_FOCUS_CLICK: just set the focus, the most simple one
 *
 * - E_FOCUS_MOUSE: focus is where the mouse is, so try to
 *   warp the pointer to the window. If this fails (because
 *   the pointer is already in the window), just set the focus.
 *
 * - E_FOCUS_SLOPPY: focus is where the mouse is or on the
 *   last window which was focused, if the mouse is on the
 *   desktop. So, we need to look if there is another window
 *   under the pointer and warp to pointer to the right
 *   one if so (also, we set the focus afterwards). In case
 *   there is no window under pointer, the pointer is on the
 *   desktop and so we just set the focus.
 *
 *
 * This function is to be called when setting the focus was not
 * explicitly triggered by the user (by moving the mouse or
 * clicking for example), but implicitly (by closing a window,
 * the last focused window should get focus).
 *
 */
E_API void
e_client_focus_set_with_pointer(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   /* 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 focuse set on such windows */
   if (e_pixmap_is_x(ec->pixmap))
     {
        /* be strict about accepting focus hint */
        if ((!ec->icccm.accepts_focus) &&
            (!ec->icccm.take_focus)) return;
     }
   ec = e_client_stack_active_adjust(ec);
   if (ec->lock_focus_out) return;
   if (ec == focused) return;
   evas_object_focus_set(ec->frame, 1);

   if (e_config->focus_policy == E_FOCUS_CLICK) return;
   if (!ec->visible) return;

   if (e_config->focus_policy == E_FOCUS_SLOPPY)
     {
        E_Client *pec;
        pec = e_client_under_pointer_get(ec->desk, ec);
        /* Do not slide pointer when disabled (probably breaks focus
         * on sloppy/mouse focus but requested by users). */
        if (e_config->pointer_slide && pec && (pec != ec))
          e_client_pointer_warp_to_center(ec);
     }
   else
     {
        if (e_config->pointer_slide)
          e_client_pointer_warp_to_center(ec);
     }
}

static Eina_Bool
_e_client_is_in_parents(E_Client *ec, E_Client *ec_find)
{
   if (ec == ec_find) return EINA_TRUE;
   if (e_object_is_del(E_OBJECT(ec))) return EINA_FALSE;
   if (ec->parent) return _e_client_is_in_parents(ec->parent, ec_find);
   return EINA_FALSE;
}

EINTERN void
e_client_focused_set(E_Client *ec)
{
   E_Client *ec2, *ec_unfocus = focused;
   Eina_List *l, *ll;

   if (ec == focused) return;
   focused = ec;
   if ((ec) && (ec->zone))
     {
        ec->focused = 1;
        e_client_urgent_set(ec, 0);
        if (!e_config->allow_above_fullscreen)
          {
             int x, total = ec->zone->desk_x_count * ec->zone->desk_y_count;

             for (x = 0; x < total; x++)
               {
                  E_Desk *desk = ec->zone->desks[x];
                  /* if there's any fullscreen non-parents on this desk, unfullscreen them */
                  EINA_LIST_FOREACH_SAFE(desk->fullscreen_clients, l, ll, ec2)
                    {
                       if (ec2 == ec) continue;
                       if (e_object_is_del(E_OBJECT(ec2))) continue;
                       /* but only if it's the same desk or one of the clients is sticky */
                       if ((desk == ec->desk) || (ec->sticky || ec2->sticky))
                         {
                            if (!_e_client_is_in_parents(ec, ec2))
                              e_client_unfullscreen(ec2);
                         }
                    }
               }
          }
     }

   while ((ec_unfocus) && (ec_unfocus->zone))
     {
        Eina_Bool is_popup, is_child;

        ec_unfocus->want_focus = ec_unfocus->focused = 0;
        if (!e_object_is_del(E_OBJECT(ec_unfocus)))
          e_focus_event_focus_out(ec_unfocus);
        if (ec_unfocus->mouse.in && ec && (!e_client_util_is_popup(ec)) &&
            (e_config->focus_policy != E_FOCUS_CLICK) && e_config->pointer_slide)
          e_client_mouse_out(ec_unfocus, ec_unfocus->x - 1, ec_unfocus->y - 1);

        E_FREE_FUNC(ec_unfocus->raise_timer, ecore_timer_del);

        is_popup = EINA_FALSE;
        is_child = EINA_FALSE;
        if ((ec) &&
            ((ec->netwm.type == E_WINDOW_TYPE_MENU) ||
             (ec->netwm.type == E_WINDOW_TYPE_TOOLBAR) ||
             (ec->netwm.type == E_WINDOW_TYPE_DOCK) ||
             (ec->netwm.type == E_WINDOW_TYPE_SPLASH) ||
             (ec->netwm.type == E_WINDOW_TYPE_TOOLTIP) ||
             (ec->netwm.type == E_WINDOW_TYPE_DROPDOWN_MENU) ||
             (ec->netwm.type == E_WINDOW_TYPE_POPUP_MENU) ||
             (ec->netwm.type == E_WINDOW_TYPE_COMBO) ||
             (ec->netwm.type == E_WINDOW_TYPE_DND) ||
             (ec->netwm.type == E_WINDOW_TYPE_NOTIFICATION)))
          is_popup = EINA_TRUE;
        if ((ec) && _e_client_is_in_parents(ec, ec_unfocus))
          is_child = EINA_TRUE;

        /* if there unfocus client is fullscreen and visible */
        if ((!is_popup) && (!is_child) &&
            (!e_config->allow_above_fullscreen) &&
            (ec_unfocus->fullscreen) && (!ec_unfocus->iconic) && (!ec_unfocus->hidden) &&
            (ec_unfocus->zone == e_zone_current_get()) &&
            ((ec_unfocus->desk == e_desk_current_get(ec_unfocus->zone)) || (ec_unfocus->sticky)))
          {
             Eina_Bool have_vis_child = EINA_FALSE;

             /* if any of its children are visible */
             EINA_LIST_FOREACH(ec_unfocus->transients, l, ec2)
               {
                  if ((ec2->zone == ec_unfocus->zone) &&
                      ((ec2->desk == ec_unfocus->desk) ||
                       (ec2->sticky) || (ec_unfocus->sticky)))
                    {
                       have_vis_child = EINA_TRUE;
                       break;
                    }
               }
             /* if no children are visible, unfullscreen */
             if ((!e_object_is_del(E_OBJECT(ec_unfocus))) && (!have_vis_child))
               e_client_unfullscreen(ec_unfocus);
          }

        _e_client_hook_call(E_CLIENT_HOOK_FOCUS_UNSET, ec_unfocus);
        /* only send event here if we're not being deleted */
        if ((!e_object_is_del(E_OBJECT(ec_unfocus))) &&
           (e_object_ref_get(E_OBJECT(ec_unfocus)) > 0))
          {
             _e_client_event_simple(ec_unfocus, E_EVENT_CLIENT_FOCUS_OUT);
             e_client_urgent_set(ec_unfocus, ec_unfocus->icccm.urgent);
          }
        break;
     }
   if (!ec) return;

   _e_client_hook_call(E_CLIENT_HOOK_FOCUS_SET, ec);
   e_focus_event_focus_in(ec);

   if (!focus_track_frozen)
     e_client_focus_latest_set(ec);

   e_hints_active_window_set(ec);
   _e_client_event_simple(ec, E_EVENT_CLIENT_FOCUS_IN);
   if (ec->sticky && ec->desk && (!ec->desk->visible))
     e_client_desk_set(ec, e_desk_current_get(ec->zone));
}

E_API void
e_client_activate(E_Client *ec, Eina_Bool just_do_it)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   ec = e_client_stack_active_adjust(ec);
   if ((e_config->focus_setting == E_FOCUS_NEW_WINDOW) ||
       ((ec->parent) &&
        ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) ||
         ((ec->parent->focused) &&
          (e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED)))) ||
       (just_do_it))
     {
        if (ec->iconic)
          {
             if (e_config->clientlist_warp_to_iconified_desktop == 1)
               e_desk_show(ec->desk);

             if (!ec->lock_user_iconify)
               e_client_uniconify(ec);
          }
        if ((!ec->iconic) && ((!ec->sticky) || e_config->focus_revert_allow_sticky))
          {
             int val = e_config->focus_last_focused_per_desktop;

             /* prevent infinite focus loops during refocus */
             if (!ec->lock_focus_out)
               e_config->focus_last_focused_per_desktop = 0;
             e_desk_show(ec->desk);
             e_config->focus_last_focused_per_desktop = val;
          }
        if (!ec->lock_user_stacking)
          evas_object_raise(ec->frame);
        if (ec->shaded || ec->shading)
          e_client_unshade(ec, ec->shade_dir);
        if (!ec->lock_focus_out)
          {
             /* XXX ooffice does send this request for
                config dialogs when the main window gets focus.
                causing the pointer to jump back and forth.  */
             if ((e_config->focus_policy != E_FOCUS_CLICK) && (!ec->new_client) &&
                 (!e_config->disable_all_pointer_warps) &&
                 (!e_util_strcmp(ec->icccm.name, "VCLSalFrame")))
               ecore_evas_pointer_warp(e_comp->ee,
                                    ec->x + (ec->w / 2), ec->y + (ec->h / 2));
             evas_object_focus_set(ec->frame, 1);
          }
     }
}

E_API E_Client *
e_client_focused_get(void)
{
   return focused;
}

E_API Eina_List *
e_client_focus_stack_get(void)
{
   return focus_stack;
}

YOLO E_API void
e_client_focus_stack_set(Eina_List *l)
{
   focus_stack = l;
}

E_API Eina_List *
e_client_raise_stack_get(void)
{
   return raise_stack;
}

E_API Eina_List *
e_client_lost_windows_get(E_Zone *zone)
{
   Eina_List *list = NULL;
   const Eina_List *l;
   E_Client *ec;
   int loss_overlap = 5;

   E_OBJECT_CHECK_RETURN(zone, NULL);
   E_OBJECT_TYPE_CHECK_RETURN(zone, E_ZONE_TYPE, NULL);
   EINA_LIST_FOREACH(e_comp->clients, l, ec)
     {
        if (ec->zone != zone) continue;
        if (e_client_util_ignored_get(ec)) continue;

        if (!E_INTERSECTS(ec->zone->x + loss_overlap,
                          ec->zone->y + loss_overlap,
                          ec->zone->w - (2 * loss_overlap),
                          ec->zone->h - (2 * loss_overlap),
                          ec->x, ec->y, ec->w, ec->h))
          {
             list = eina_list_append(list, ec);
          }
     }
   return list;
}

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

E_API void
e_client_shade(E_Client *ec, E_Direction dir)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (((!ec->shaded) && ec->shading) || (ec->shaded && (!ec->shading)) || (ec->fullscreen) ||
       ((ec->maximized) && (!e_config->allow_manip))) return;
   if (!e_util_strcmp("borderless", ec->bordername)) return;
   if (!e_comp_object_frame_allowed(ec->frame)) return;

   e_hints_window_shaded_set(ec, 1);
   e_hints_window_shade_direction_set(ec, dir);
   ec->take_focus = 0;
   ec->shading = 1;
   ec->shade_dir = dir;

   if (e_config->border_shade_animate && (!ec->new_client))
     {
        ec->changes.shading = 1;
        EC_CHANGED(ec);

        evas_object_smart_callback_call(ec->frame, "shading", (uintptr_t*)dir);
     }
   else
     evas_object_smart_callback_call(ec->frame, "shaded", (uintptr_t*)dir);

   e_remember_update(ec);
}

E_API void
e_client_unshade(E_Client *ec, E_Direction dir)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if ((!ec->shaded) || (ec->shading))
     return;

   e_hints_window_shaded_set(ec, 0);
   e_hints_window_shade_direction_set(ec, dir);
   ec->shading = 1;
   ec->shade_dir = 0;

   if (e_config->border_shade_animate)
     {
        ec->changes.shading = 1;
        EC_CHANGED(ec);

        evas_object_smart_callback_call(ec->frame, "unshading", (uintptr_t*)dir);
     }
   else
     evas_object_smart_callback_call(ec->frame, "unshaded", (uintptr_t*)dir);

   e_remember_update(ec);
}

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

E_API Eina_Bool
e_client_maximize_geometry_get(const E_Client *ec, E_Maximize max, int *mx, int *my, int *mw, int *mh)
{
   int x1, yy1, x2, y2;
   int x = 0, y = 0, w, h, pw, ph;
   int zx, zy, zw, zh;
   int ecx, ecy, ecw, ech;

   if (e_client_util_ignored_get(ec)) return EINA_FALSE;
   zx = zy = zw = zh = 0;

   switch (max & E_MAXIMIZE_TYPE)
     {
      case E_MAXIMIZE_FULLSCREEN:
        w = ec->zone->w;
        h = ec->zone->h;

        e_client_resize_limit(ec, &w, &h);
        /* center x-direction */
        x1 = ec->zone->x + (ec->zone->w - w) / 2;
        /* center y-direction */
        yy1 = ec->zone->y + (ec->zone->h - h) / 2;

        switch (max & E_MAXIMIZE_DIRECTION)
          {
           case E_MAXIMIZE_BOTH:
             x = x1, y = yy1;
             break;

           case E_MAXIMIZE_VERTICAL:
             x = ec->x, y = yy1, w = ec->w;
             break;

           case E_MAXIMIZE_HORIZONTAL:
             x = x1, y = ec->y, h = ec->h;
             break;

           case E_MAXIMIZE_LEFT:
             x = ec->zone->x, y = ec->zone->y, w /= 2;
             break;

           case E_MAXIMIZE_RIGHT:
             x = x1, y = ec->zone->y, w /= 2;
             break;
          }
        if (mx) *mx = x;
        if (my) *my = y;
        if (mw) *mw = w;
        if (mh) *mh = h;
        break;

      case E_MAXIMIZE_SMART:
      case E_MAXIMIZE_EXPAND:
        e_zone_desk_useful_geometry_get(ec->zone, ec->desk, &zx, &zy, &zw, &zh);
        w = zw, h = zh;

        e_comp_object_frame_xy_unadjust(ec->frame, ec->x, ec->y, &ecx, &ecy);
        e_comp_object_frame_wh_unadjust(ec->frame, ec->w, ec->h, &ecw, &ech);

        if (ecw < zw)
          w = ecw;

        if (ech < zh)
          h = ech;

        if (ecx < zx) // window left not useful coordinates
          x1 = zx;
        else if (ecx + ecw > zx + zw) // window right not useful coordinates
          x1 = zx + zw - ecw;
        else // window normal position
          x1 = ecx;

        if (ecy < zy) // window top not useful coordinates
          yy1 = zy;
        else if (ecy + ech > zy + zh) // window bottom not useful coordinates
          yy1 = zy + zh - ech;
        else // window normal position
          yy1 = ecy;

        switch (max & E_MAXIMIZE_DIRECTION)
          {
           case E_MAXIMIZE_BOTH:
             x = zx, y = zy, w = zw, h = zh;
             break;

           case E_MAXIMIZE_VERTICAL:
             x = ec->x, y = zy, w = ec->w, h = zh;
             break;

           case E_MAXIMIZE_HORIZONTAL:
             x = zx, y = ec->y, w = zw, h = ec->h;
             break;

           case E_MAXIMIZE_LEFT:
             x = zx, y = zy, w = zw / 2, h = zh;
             break;

           case E_MAXIMIZE_RIGHT:
             x = zx + zw / 2, y = zy, w = zw / 2, h = zh;
             break;
          }
        break;

      case E_MAXIMIZE_FILL:
        x1 = ec->zone->x;
        yy1 = ec->zone->y;
        x2 = ec->zone->x + ec->zone->w;
        y2 = ec->zone->y + ec->zone->h;

        e_zone_desk_useful_geometry_get(ec->zone, ec->desk, &zx, &zy, &zw, &zh);
        x1 = zx, yy1 = zy, x2 = x1 + zw, y2 = yy1 + zh;

        w = x2 - x1;
        h = y2 - yy1;
        pw = w;
        ph = h;
        e_client_resize_limit(ec, &w, &h);
        /* center x-direction */
        x1 = x1 + (pw - w) / 2;
        /* center y-direction */
        yy1 = yy1 + (ph - h) / 2;

        switch (max & E_MAXIMIZE_DIRECTION)
          {
           case E_MAXIMIZE_BOTH:
             x = x1, y = yy1;
             break;

           case E_MAXIMIZE_VERTICAL:
             x = ec->x, y = yy1, w = ec->w;
             break;

           case E_MAXIMIZE_HORIZONTAL:
             x = x1, y = ec->y, h = ec->h;
             break;

           case E_MAXIMIZE_LEFT:
             x = ec->zone->x, y = ec->zone->y, w /= 2;
             break;

           case E_MAXIMIZE_RIGHT:
             x = x1, y = ec->zone->y, w /= 2;
             break;
          }
        break;
      default:
        return EINA_FALSE;
     }
   if (mx) *mx = x;
   if (my) *my = y;
   if (mw) *mw = w;
   if (mh) *mh = h;
   return EINA_TRUE;
}

E_API void
e_client_maximize(E_Client *ec, E_Maximize max)
{
   Eina_Bool override;
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (!ec->zone) return;
   if (ec->maximized == max) return;
   if (!(max & E_MAXIMIZE_DIRECTION)) max |= E_MAXIMIZE_BOTH;

   if ((ec->shaded) || (ec->shading)) return;

   /* Only allow changes in vertical/ horizontal maximization */
   if (((ec->maximized & E_MAXIMIZE_DIRECTION) == (max & E_MAXIMIZE_DIRECTION)) ||
       ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_BOTH)) return;
   if (ec->new_client)
     {
        ec->changes.need_maximize = 1;
        ec->maximized &= ~E_MAXIMIZE_TYPE;
        ec->maximized |= max;
        EC_CHANGED(ec);
        if (ec->re_manage) ec->changes.pos = 0;
        return;
     }
   if ((max & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN)
     evas_object_smart_callback_call(ec->frame, "fullscreen", NULL);
   else
     evas_object_smart_callback_call(ec->frame, "maximize", NULL);
   evas_object_smart_callback_call(ec->frame, "maximize_pre", &max);
   if (!max) return;
   if (ec->moving)
     {
        if (ec == action_client)
          e_comp_canvas_feed_mouse_up(0); //triggers event grabber cb
     }
   override = ec->maximize_override;
   if (ec->fullscreen)
     e_client_unfullscreen(ec);
   ec->pre_res_change.valid = 0;
   if (!ec->saved.set)
     {
        if (!(ec->maximized & E_MAXIMIZE_HORIZONTAL))
          {
             /* Horizontal hasn't been set */
             if (ec->changes.pos)
               e_comp_object_frame_xy_adjust(ec->frame, ec->x, 0, &ec->saved.x, NULL);
             else
               ec->saved.x = ec->client.x;
             ec->saved.x -= ec->zone->x;
             if (ec->visible)
               ec->saved.w = ec->client.w;
          }
        if (!(ec->maximized & E_MAXIMIZE_VERTICAL))
          {
             /* Vertical hasn't been set */
             if (ec->changes.pos)
               e_comp_object_frame_xy_adjust(ec->frame, 0, ec->y, NULL, &ec->saved.y);
             else
               ec->saved.y = ec->client.y;
             ec->saved.y -= ec->zone->y;
             if (ec->visible)
               ec->saved.h = ec->client.h;
          }
        ec->saved.zone = ec->zone->num;
        ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame));
     }

   ec->maximize_override = 1;

   {
      int x, y, w, h;
      e_client_maximize_geometry_get(ec, max, &x, &y, &w, &h);
      if (!_e_client_maximize_run(ec, x, y, w, h))
        ec->maximize_override = override;
   }

   /* Remove previous type */
   ec->maximized &= ~E_MAXIMIZE_TYPE;
   /* Add new maximization. It must be added, so that VERTICAL + HORIZONTAL == BOTH */
   ec->maximized |= max;
   ec->changes.need_unmaximize = 0;

   if ((ec->maximized & E_MAXIMIZE_DIRECTION) > E_MAXIMIZE_BOTH)
     /* left/right maximize */
     e_hints_window_maximized_set(ec, 0,
                                  ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_LEFT) ||
                                  ((ec->maximized & E_MAXIMIZE_DIRECTION) == E_MAXIMIZE_RIGHT));
   else
     e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL,
                                  ec->maximized & E_MAXIMIZE_VERTICAL);
   e_remember_update(ec);
   evas_object_smart_callback_call(ec->frame, "maximize_done", NULL);
}

E_API Eina_Bool
e_client_unmaximize_geometry_get(const E_Client *ec, E_Maximize max, int *mx, int *my, int *mw, int *mh)
{
   int w, h, x, y;

   if (e_client_util_ignored_get(ec)) return EINA_FALSE;
   if (!(ec->maximized & E_MAXIMIZE_TYPE)) return EINA_FALSE;
   if ((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN)
     {
        if (mx) *mx = ec->saved.x + ec->zone->x;
        if (my) *my = ec->saved.y + ec->zone->y;
        if (mw) *mw = ec->saved.w;
        if (mh) *mh = ec->saved.h;
        return EINA_TRUE;
     }

   w = ec->client.w;
   h = ec->client.h;
   x = ec->client.x;
   y = ec->client.y;
   max &= (ec->maximized & E_MAXIMIZE_DIRECTION);

   if (max & E_MAXIMIZE_VERTICAL)
     {
        /* Remove vertical */
        h = ec->saved.h;
        y = ec->saved.y + ec->zone->y;
     }
   if (max & E_MAXIMIZE_HORIZONTAL)
     {
        /* Remove horizontal */
        w = ec->saved.w;
        x = ec->saved.x + ec->zone->x;
     }
   if (mx) *mx = x;
   if (my) *my = y;
   if (mw) *mw = w;
   if (mh) *mh = h;
   return EINA_TRUE;
}

E_API void
e_client_unmaximize(E_Client *ec, E_Maximize max)
{
   E_Maximize unmax = max;
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (!(max & E_MAXIMIZE_DIRECTION))
     {
        CRI("BUG: Unmaximize call without direction!");
        return;
     }
   if (ec->new_client)
     {
        ec->changes.need_unmaximize = 1;
        EC_CHANGED(ec);
        return;
     }

   if ((ec->shaded) || (ec->shading)) return;

   /* Remove directions not used */
   unmax &= (ec->maximized & E_MAXIMIZE_DIRECTION);
   evas_object_smart_callback_call(ec->frame, "unmaximize_pre", &unmax);
   /* Can only remove existing maximization directions */
   if ((!unmax) && (!ec->need_fullscreen)) return;
   if (!unmax) unmax = max & (ec->maximized & E_MAXIMIZE_DIRECTION);
   if (ec->maximized & E_MAXIMIZE_TYPE)
     {
        ec->pre_res_change.valid = 0;
        ec->changes.need_maximize = 0;

        if ((ec->maximized & E_MAXIMIZE_TYPE) == E_MAXIMIZE_FULLSCREEN)
          {
             E_Maximize tmp_max = ec->maximized;

             //un-set maximized state for updating frame.
             ec->maximized = E_MAXIMIZE_NONE;
             _e_client_frame_update(ec);
             // re-set maximized state for unmaximize smart callback.
             ec->maximized = tmp_max;
             evas_object_smart_callback_call(ec->frame, "unfullscreen", NULL);
             // un-set maximized state.
             ec->maximized = E_MAXIMIZE_NONE;
             e_client_util_move_resize_without_frame(ec,
                                                     ec->saved.x + ec->zone->x,
                                                     ec->saved.y + ec->zone->y,
                                                     ec->saved.w, ec->saved.h);
             ec->saved.x = ec->saved.y = ec->saved.w = ec->saved.h = 0;
             ec->saved.set = 0;
             e_hints_window_size_unset(ec);
          }
        else
          {
             int w, h, x, y;
             Eina_Bool horiz = EINA_FALSE, vert = EINA_FALSE;
             Eina_Bool fullscreen = !!eina_list_data_find(ec->desk->fullscreen_clients, ec);

             e_client_unmaximize_geometry_get(ec, unmax, &x, &y, &w, &h);
             if (unmax & E_MAXIMIZE_VERTICAL)
               {
                  /* Remove vertical */
                  vert = EINA_TRUE;
                  if ((unmax & E_MAXIMIZE_VERTICAL) == E_MAXIMIZE_VERTICAL)
                    {
                       if ((ec->maximized & E_MAXIMIZE_LEFT) == E_MAXIMIZE_LEFT)
                         ec->maximized &= ~E_MAXIMIZE_LEFT;
                       if ((ec->maximized & E_MAXIMIZE_RIGHT) == E_MAXIMIZE_RIGHT)
                         ec->maximized &= ~E_MAXIMIZE_RIGHT;
                       ec->maximized &= ~E_MAXIMIZE_VERTICAL;
                    }
                  if ((unmax & E_MAXIMIZE_LEFT) == E_MAXIMIZE_LEFT)
                    ec->maximized &= ~E_MAXIMIZE_LEFT;
                  if ((unmax & E_MAXIMIZE_RIGHT) == E_MAXIMIZE_RIGHT)
                    ec->maximized &= ~E_MAXIMIZE_RIGHT;
               }
             if (unmax & E_MAXIMIZE_HORIZONTAL)
               {
                  /* Remove horizontal */
                  horiz = EINA_TRUE;
                  ec->maximized &= ~E_MAXIMIZE_HORIZONTAL;
               }

             if (!(ec->maximized & E_MAXIMIZE_DIRECTION))
               {
                  ec->maximized = E_MAXIMIZE_NONE;
                  _e_client_frame_update(ec);
                  e_hints_window_size_unset(ec);
               }
             if (e_config->window_maximize_animate && (!ec->maximize_anims_disabled))
               ec->maximize_override = 1;
             if (!fullscreen)
               evas_object_smart_callback_call(ec->frame, "unmaximize", NULL);
             if (ec->saved.frame &&
               (e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame))))
               {
                  e_comp_object_frame_xy_adjust(ec->frame, x, y, &x, &y);
                  e_comp_object_frame_wh_adjust(ec->frame, w, h, &w, &h);
               }
             e_client_resize_limit(ec, &w, &h);
             if (fullscreen)
               evas_object_smart_callback_call(ec->frame, "unmaximize", NULL);
             if (!_e_client_maximize_run(ec, x, y, w, h))
               ec->maximize_override = 0;
             if (vert)
               ec->saved.h = ec->saved.y = 0;
             if (horiz)
               ec->saved.w = ec->saved.x = 0;
             if (vert && horiz)
               ec->saved.set = ec->saved.frame = 0;
          }
        e_hints_window_maximized_set(ec, ec->maximized & E_MAXIMIZE_HORIZONTAL,
                                     ec->maximized & E_MAXIMIZE_VERTICAL);
     }
   e_remember_update(ec);
   evas_object_smart_callback_call(ec->frame, "unmaximize_done", NULL);
   ec->changes.need_unmaximize = 0;
}

E_API void
e_client_fullscreen(E_Client *ec, E_Fullscreen policy)
{
   int x = 0, y = 0, w = 0, h = 0;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;

   if ((ec->shaded) || (ec->shading) || ec->fullscreen) return;
   if ((!e_config->allow_above_fullscreen) && (!ec->desk->visible)) return;
   ec->need_fullscreen = 1;
   if (ec->new_client) return;
   if (e_comp->nocomp_ec && (ec->desk == e_comp->nocomp_ec->desk))
     {
        e_object_unref(E_OBJECT(e_comp->nocomp_ec));
        e_object_ref(E_OBJECT(ec));
        e_comp->nocomp_ec = ec;
     }
   ec->desk->fullscreen_clients = eina_list_append(ec->desk->fullscreen_clients, ec);
   ec->pre_res_change.valid = 0;

   if (ec->maximized)
     {
        x = ec->saved.x;
        y = ec->saved.y;
        w = ec->saved.w;
        h = ec->saved.h;
     }
   else
     {
        if (ec->changes.pos)
          e_comp_object_frame_xy_adjust(ec->frame, ec->x, ec->y, &ec->saved.x, &ec->saved.y);
        else
          {
             ec->saved.x = ec->client.x;
             ec->saved.y = ec->client.y;
          }
        ec->saved.x -= ec->zone->x;
        ec->saved.y -= ec->zone->y;

        if (ec->visible)
          {
             ec->saved.w = ec->client.w;
             ec->saved.h = ec->client.h;
          }
     }
   E_FREE_FUNC(ec->agent, evas_object_del);
   ec->saved.maximized = ec->maximized;
   ec->saved.zone = ec->zone->num;
   ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame));

   if (ec->maximized)
     {
        Eina_Bool maximize_anims_disabled = ec->maximize_anims_disabled;

        ec->maximize_anims_disabled = 1;
        e_client_unmaximize(ec, E_MAXIMIZE_BOTH);
        ec->maximize_anims_disabled = maximize_anims_disabled;
        ec->saved.x = x;
        ec->saved.y = y;
        ec->saved.w = w;
        ec->saved.h = h;
        ec->saved.frame = e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame));
     }

   ec->saved.layer = ec->layer;
   ec->saved.set = 1;
   if (!e_config->allow_above_fullscreen)
     evas_object_layer_set(ec->frame, E_LAYER_CLIENT_FULLSCREEN);
   else if (e_config->mode.presentation)
     evas_object_layer_set(ec->frame, E_LAYER_CLIENT_TOP);

   ec->fullscreen = 1;
#ifndef HAVE_WAYLAND_ONLY
   if ((eina_list_count(e_comp->zones) > 1) ||
       (policy == E_FULLSCREEN_RESIZE) || (!ecore_x_randr_query()))
#else
   if ((eina_list_count(e_comp->zones) > 1) ||
       (policy == E_FULLSCREEN_RESIZE))
#endif
     {
        evas_object_geometry_set(ec->frame, ec->zone->x, ec->zone->y, ec->zone->w, ec->zone->h);
     }
   else if (policy == E_FULLSCREEN_ZOOM)
     {
        /* compositor backends! */
        evas_object_smart_callback_call(ec->frame, "fullscreen_zoom", NULL);
     }

   e_hints_window_fullscreen_set(ec, 1);
   e_hints_window_size_unset(ec);
   if (!e_client_util_ignored_get(ec))
     _e_client_frame_update(ec);
   ec->fullscreen_policy = policy;
   evas_object_smart_callback_call(ec->frame, "fullscreen", NULL);

   _e_client_event_simple(ec, E_EVENT_CLIENT_FULLSCREEN);

   e_remember_update(ec);
   ec->need_fullscreen = 0;
}

E_API void
e_client_unfullscreen(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if ((ec->shaded) || (ec->shading)) return;
   if (!ec->fullscreen) return;
   ec->pre_res_change.valid = 0;
   ec->fullscreen = 0;
   ec->need_fullscreen = 0;
   ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec);

   if (ec->fullscreen_policy == E_FULLSCREEN_ZOOM)
     evas_object_smart_callback_call(ec->frame, "unfullscreen_zoom", NULL);

   if (!e_client_util_ignored_get(ec))
     _e_client_frame_update(ec);
   ec->fullscreen_policy = 0;
   evas_object_smart_callback_call(ec->frame, "unfullscreen", NULL);

   if (ec->saved.maximized)
     {
        Eina_Bool maximize_anims_disabled = ec->maximize_anims_disabled;
        ec->changes.pos = ec->changes.size = 0;
        ec->maximize_anims_disabled = 1;
        e_client_maximize(ec,
          (e_config->maximize_policy & E_MAXIMIZE_TYPE) | ec->saved.maximized);
        ec->maximize_anims_disabled = maximize_anims_disabled;
     }
   else
     {
        if (ec->saved.frame &&
          (e_comp_object_frame_exists(ec->frame) || (!e_comp_object_frame_allowed(ec->frame))))
          e_client_util_move_resize_without_frame(ec, ec->zone->x + ec->saved.x,
                                                  ec->zone->y + ec->saved.y,
                                                  ec->saved.w, ec->saved.h);
        else
           evas_object_geometry_set(ec->frame, ec->zone->x + ec->saved.x,
                                                   ec->zone->y + ec->saved.y,
                                                   ec->saved.w, ec->saved.h);
        ec->saved.w = ec->saved.x = ec->saved.h = ec->saved.y = 0;
        ec->saved.set = ec->saved.frame = 0;
     }

   evas_object_layer_set(ec->frame, ec->saved.layer);

   e_hints_window_fullscreen_set(ec, 0);
   _e_client_event_simple(ec, E_EVENT_CLIENT_UNFULLSCREEN);

   e_remember_update(ec);
   if (!ec->desk->fullscreen_clients)
     e_comp_render_queue();
}

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


E_API void
e_client_iconify(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->shading || ec->iconic) return;
   if (((ec->stack.prev || ec->stack.next)) && (!ec->stack.ignore))
     {
        Eina_List *l, *list = e_client_stack_list_prepare(ec);
        E_Client *child;

        EINA_LIST_FOREACH(list, l, child)
          {
             e_client_iconify(child);
          }
        e_client_stack_list_finish(list);
        E_Desk *desk;

        desk = e_desk_current_get(ec->zone);
        e_desk_last_focused_focus(desk);
        return;
     }
   ec->iconic = 1;
   ec->want_focus = ec->take_focus = 0;
   ec->changes.visible = 0;
   if (ec->fullscreen)
     ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec);
   e_client_comp_hidden_set(ec, 1);
   if (!ec->stack.ignore)
     {
        if (!ec->new_client)
          {
             _e_client_revert_focus(ec);
             evas_object_hide(ec->frame);
          }
        e_client_urgent_set(ec, ec->icccm.urgent);
     }
   else
     {
        if (!ec->new_client)
          evas_object_hide(ec->frame);
        e_client_urgent_set(ec, ec->icccm.urgent);
        if (ec->focused)
          evas_object_focus_set(ec->frame, 0);
     }

   _e_client_event_simple(ec, E_EVENT_CLIENT_ICONIFY);

   if (!ec->stack.prev && !ec->stack.next)
     {
        if (e_config->transient.iconify)
          {
             E_Client *child;
             Eina_List *list = eina_list_clone(ec->transients);

             EINA_LIST_FREE(list, child)
               e_client_iconify(child);
          }
     }
   e_remember_update(ec);
}

E_API void
e_client_uniconify(E_Client *ec)
{
   E_Desk *desk;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->shading || (!ec->iconic)) return;

   if (((ec->stack.prev || ec->stack.next)) && (!ec->stack.ignore))
     {
        Eina_List *l, *list = e_client_stack_list_prepare(ec);
        E_Client *child, *ec_focus = NULL;

        EINA_LIST_FOREACH(list, l, child)
          {
             e_client_uniconify(child);
             if (!l->next) ec_focus = child;
          }
        e_client_stack_list_finish(list);
        evas_object_raise(ec_focus->frame);
        evas_object_focus_set(ec_focus->frame, 1);
        return;
     }
   desk = e_desk_current_get(ec->desk->zone);
   e_client_desk_set(ec, desk);
   if (!ec->stack.ignore)
     evas_object_raise(ec->frame);
   evas_object_show(ec->frame);
   e_client_comp_hidden_set(ec, 0);
   ec->deskshow = ec->iconic = 0;
   if (!ec->stack.ignore)
     evas_object_focus_set(ec->frame, 1);

   _e_client_event_simple(ec, E_EVENT_CLIENT_UNICONIFY);

   if (!ec->stack.prev && !ec->stack.next)
     {
        if (e_config->transient.iconify)
          {
             E_Client *child;
             Eina_List *list = eina_list_clone(ec->transients);

             EINA_LIST_FREE(list, child)
               e_client_uniconify(child);
          }
     }
   e_remember_update(ec);
}

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

E_API void
e_client_urgent_set(E_Client *ec, Eina_Bool urgent)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (urgent && e_screensaver_on_get() && e_config->screensaver_wake_on_urgent)
     {
        int x, y;
        ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
        ecore_evas_pointer_warp(e_comp->ee, x, y);
        e_comp_canvas_notidle();
     }
   if (!ec->zone) return;

   urgent = !!urgent;
   if (urgent == ec->urgent) return;
   _e_client_event_property(ec, E_CLIENT_PROPERTY_URGENCY);
   if (urgent && (!ec->focused) && (!ec->want_focus))
     {
        e_comp_object_signal_emit(ec->frame, "e,state,urgent", "e");
        ec->urgent = urgent;
     }
   else
     {
        e_comp_object_signal_emit(ec->frame, "e,state,not_urgent", "e");
        ec->urgent = 0;
     }
}

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

E_API void
e_client_stick(E_Client *ec)
{
   E_Desk *desk;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->sticky) return;
   desk = ec->desk;
   ec->desk = NULL;
   if (desk && ec->fullscreen)
     desk->fullscreen_clients = eina_list_remove(desk->fullscreen_clients, ec);
   ec->sticky = 1;
   ec->hidden = 0;
   e_hints_window_sticky_set(ec, 1);
   e_client_desk_set(ec, desk);
   evas_object_smart_callback_call(ec->frame, "stick", NULL);

   if (ec->stack.prev || ec->stack.next)
     {
        if (ec->stack.ignore == 0)
          {
             Eina_List *l, *list = e_client_stack_list_prepare(ec);
             E_Client *child;

             EINA_LIST_FOREACH(list, l, child)
               {
                  if (child == ec) continue;
                  child->sticky = 1;
                  e_hints_window_sticky_set(child, 1);
                  evas_object_show(ec->frame);
               }
             e_client_stack_list_finish(list);
          }
     }
   else
     {
        if (e_config->transient.desktop)
          {
             E_Client *child;
             Eina_List *list = eina_list_clone(ec->transients);

             EINA_LIST_FREE(list, child)
               {
                  child->sticky = 1;
                  e_hints_window_sticky_set(child, 1);
                  evas_object_show(ec->frame);
               }
          }
     }

   _e_client_event_property(ec, E_CLIENT_PROPERTY_STICKY);
   e_remember_update(ec);
}

E_API void
e_client_unstick(E_Client *ec)
{
   E_Desk *desk;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   /* Set the desk before we unstick the client */
   if (!ec->sticky) return;
   desk = e_desk_current_get(ec->zone);
   if (ec->desk && ec->fullscreen)
     ec->desk->fullscreen_clients = eina_list_remove(ec->desk->fullscreen_clients, ec);
   ec->desk = NULL;
   ec->hidden = ec->sticky = 0;
   e_hints_window_sticky_set(ec, 0);
   e_client_desk_set(ec, desk);
   evas_object_smart_callback_call(ec->frame, "unstick", NULL);

   if (ec->stack.prev || ec->stack.next)
     {
        if (ec->stack.ignore == 0)
          {
             Eina_List *l, *list = e_client_stack_list_prepare(ec);
             E_Client *child;

             EINA_LIST_FOREACH(list, l, child)
               {
                  if (child == ec) continue;
                  child->sticky = 1;
                  e_hints_window_sticky_set(child, 0);
               }
             e_client_stack_list_finish(list);
          }
     }
   else
     {
        if (e_config->transient.desktop)
          {
             E_Client *child;
             Eina_List *list = eina_list_clone(ec->transients);

             EINA_LIST_FREE(list, child)
               {
                  child->sticky = 0;
                  e_hints_window_sticky_set(child, 0);
               }
          }
     }

   _e_client_event_property(ec, E_CLIENT_PROPERTY_STICKY);

   e_client_desk_set(ec, e_desk_current_get(ec->zone));
   e_remember_update(ec);
}

E_API void
e_client_pinned_set(E_Client *ec, Eina_Bool set)
{
   E_Layer layer;

   EINA_SAFETY_ON_NULL_RETURN(ec);
   ec->borderless = !!set;
   ec->user_skip_winlist = !!set;
   if (set)
     layer = E_LAYER_CLIENT_BELOW;
   else
     layer = E_LAYER_CLIENT_NORMAL;

   evas_object_layer_set(ec->frame, layer);

   ec->border.changed = 1;
   EC_CHANGED(ec);
}

E_API void
e_client_prop_misc_changed(E_Client *ec)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   _e_client_event_property(ec, E_CLIENT_PROPERTY_MISC);
}

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

E_API Eina_Bool
e_client_border_set(E_Client *ec, const char *name)
{
   Eina_Stringshare *pborder;

   E_OBJECT_CHECK_RETURN(ec, EINA_FALSE);
   E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, EINA_FALSE);
   if ((!e_comp_object_frame_allowed(ec->frame)) && (!e_comp_object_frame_exists(ec->frame)))
     return EINA_FALSE;
   if (ec->border.changed)
     CRI("CALLING WHEN border.changed SET!");

   if (eina_streq(ec->border.name, name)) return EINA_TRUE;
   if (ec->mwm.borderless && (!eina_streq(name, "borderless")))
     {
        e_util_dialog_show(_("Client Error!"), _("Something has attempted to set a border when it shouldn't! Report this!"));
        CRI("border change attempted for MWM borderless client!");
     }
   if ((!ec->border.name) && eina_streq(name, "borderless")) return EINA_TRUE;
   pborder = ec->border.name;
   ec->border.name = eina_stringshare_add(name);
   if (e_comp_object_frame_theme_set(ec->frame, name))
     {
        eina_stringshare_del(pborder);
        return EINA_TRUE;
     }
   eina_stringshare_del(ec->border.name);
   ec->border.name = pborder;
   return EINA_FALSE;
}

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

E_API void
e_client_comp_hidden_set(E_Client *ec, Eina_Bool hidden)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;

   hidden = !!hidden;
   if (ec->comp_hidden == hidden) return;
   ec->comp_hidden = hidden;
   evas_object_smart_callback_call(ec->frame, "comp_hidden", NULL);
}

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

E_API void
e_client_act_move_keyboard(E_Client *ec)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (!ec->zone) return;

   if (!_e_client_move_begin(ec))
     return;
   _e_client_action_init(ec);
   if (!_e_client_hook_call(E_CLIENT_HOOK_MOVE_UPDATE, ec)) return;

   if (!action_handler_key)
     action_handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _e_client_move_key_down, NULL);

   if (!action_handler_mouse)
     action_handler_mouse = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_client_move_mouse_down, NULL);
   _e_client_action_move_timeout_add();
}

E_API void
e_client_act_resize_keyboard(E_Client *ec)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (!ec->zone) return;

   ec->resize_mode = E_POINTER_RESIZE_BR;
   ec->keyboard_resizing = 1;
   if (!e_client_resize_begin(ec))
     {
        ec->keyboard_resizing = 0;
        return;
     }
   _e_client_action_init(ec);

   if (!action_handler_key)
     action_handler_key = ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, _e_client_resize_key_down, NULL);

   if (!action_handler_mouse)
     action_handler_mouse = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN, _e_client_resize_mouse_down, NULL);
   _e_client_action_resize_timeout_add();
}

E_API void
e_client_act_move_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (e_client_util_resizing_get(ec) || (ec->moving)) return;
   if (ev)
     {
        char source[256];

        snprintf(source, sizeof(source) - 1, "mouse,down,%i", ev->button);
        _e_client_moveinfo_gather(ec, source);
     }
   if (!_e_client_move_begin(ec))
     return;

   _e_client_action_init(ec);
   e_pointer_mode_push(ec, E_POINTER_MOVE);
}

E_API void
e_client_act_move_end(E_Client *ec, E_Binding_Event_Mouse_Button *ev EINA_UNUSED)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (!ec->moving) return;
   e_zone_edge_enable();
   _e_client_move_end(ec);
   e_zone_flip_coords_handle(ec->zone, -1, -1);
   _e_client_action_finish();
}

E_API void
e_client_act_resize_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->lock_user_size || ec->shaded || ec->shading) return;
   if (e_client_util_resizing_get(ec) || (ec->moving)) return;
   if (ev)
     {
        char source[256];

        snprintf(source, sizeof(source) - 1, "mouse,down,%i", ev->button);
        _e_client_moveinfo_gather(ec, source);
     }
   if ((ec->mouse.current.mx > (ec->x + ec->w / 5)) &&
       (ec->mouse.current.mx < (ec->x + ec->w * 4 / 5)))
     {
        if (ec->mouse.current.my < (ec->y + ec->h / 2))
          {
             ec->resize_mode = E_POINTER_RESIZE_T;
          }
        else
          {
             ec->resize_mode = E_POINTER_RESIZE_B;
          }
     }
   else if (ec->mouse.current.mx < (ec->x + ec->w / 2))
     {
        if ((ec->mouse.current.my > (ec->y + ec->h / 5)) &&
            (ec->mouse.current.my < (ec->y + ec->h * 4 / 5)))
          {
             ec->resize_mode = E_POINTER_RESIZE_L;
          }
        else if (ec->mouse.current.my < (ec->y + ec->h / 2))
          {
             ec->resize_mode = E_POINTER_RESIZE_TL;
          }
        else
          {
             ec->resize_mode = E_POINTER_RESIZE_BL;
          }
     }
   else
     {
        if ((ec->mouse.current.my > (ec->y + ec->h / 5)) &&
            (ec->mouse.current.my < (ec->y + ec->h * 4 / 5)))
          {
             ec->resize_mode = E_POINTER_RESIZE_R;
          }
        else if (ec->mouse.current.my < (ec->y + ec->h / 2))
          {
             ec->resize_mode = E_POINTER_RESIZE_TR;
          }
        else
          {
             ec->resize_mode = E_POINTER_RESIZE_BR;
          }
     }
   if (!e_client_resize_begin(ec))
     return;
   _e_client_action_init(ec);
   e_pointer_mode_push(ec, ec->resize_mode);
}

E_API void
e_client_act_resize_end(E_Client *ec, E_Binding_Event_Mouse_Button *ev EINA_UNUSED)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (e_client_util_resizing_get(ec))
     {
        _e_client_resize_end(ec);
        ec->changes.reset_gravity = 1;
        if (!e_object_is_del(E_OBJECT(ec)))
          EC_CHANGED(ec);
     }
   _e_client_action_finish();
}

E_API void
e_client_act_menu_begin(E_Client *ec, E_Binding_Event_Mouse_Button *ev, int key)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->border_menu) return;
   if (ev)
     e_int_client_menu_show(ec, ev->canvas.x, ev->canvas.y, key, ev->timestamp);
   else
     {
        int x, y;

        evas_pointer_canvas_xy_get(e_comp->evas, &x, &y);
        e_int_client_menu_show(ec, x, y, key, 0);
     }
}

E_API void
e_client_act_close_begin(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->lock_close) return;
   if (ec->icccm.delete_request)
     {
        ec->delete_requested = 1;
        evas_object_smart_callback_call(ec->frame, "delete_request", NULL);
     }
   else if (e_config->kill_if_close_not_possible)
     {
        e_client_act_kill_begin(ec);
     }
}

E_API void
e_client_act_kill_begin(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (ec->internal) return;
   if (ec->lock_close) return;
   if (ec->netwm.pid == getpid()) return;
   if ((ec->netwm.pid > 1) && (e_config->kill_process))
     {
        kill(ec->netwm.pid, SIGINT);
        ec->kill_timer = ecore_timer_loop_add(e_config->kill_timer_wait,
                                         _e_client_cb_kill_timer, ec);
     }
   else
     evas_object_smart_callback_call(ec->frame, "kill_request", NULL);
}

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


E_API Evas_Object *
e_client_icon_add(E_Client *ec, Evas *evas)
{
   Evas_Object *o;

   E_OBJECT_CHECK_RETURN(ec, NULL);
   E_OBJECT_TYPE_CHECK_RETURN(ec, E_CLIENT_TYPE, NULL);

   o = NULL;
   if (ec->internal)
     {
        if (!ec->internal_icon)
          {
             o = e_icon_add(evas);
             e_util_icon_theme_set(o, "enlightenment");
          }
        else
          {
             if (!ec->internal_icon_key)
               {
                  char *ext;

                  ext = strrchr(ec->internal_icon, '.');
                  if ((ext) && ((!strcmp(ext, ".edj"))))
                    {
                       o = edje_object_add(evas);
                       if (!edje_object_file_set(o, ec->internal_icon, "icon"))
                         e_util_icon_theme_set(o, "enlightenment");
                    }
                  else if (ext)
                    {
                       o = e_icon_add(evas);
                       e_icon_file_set(o, ec->internal_icon);
                    }
                  else
                    {
                       o = e_icon_add(evas);
                       if (!e_util_icon_theme_set(o, ec->internal_icon))
                         e_util_icon_theme_set(o, "enlightenment");
                    }
               }
             else
               {
                  o = edje_object_add(evas);
                  edje_object_file_set(o, ec->internal_icon,
                                       ec->internal_icon_key);
               }
          }
        return o;
     }
#ifndef HAVE_WAYLAND_ONLY
   if ((e_config->use_app_icon) && (ec->icon_preference != E_ICON_PREF_USER))
     {
        if (ec->netwm.icons)
          {
             o = e_icon_add(evas);
             e_icon_data_set(o, ec->netwm.icons[0].data,
                             ec->netwm.icons[0].width,
                             ec->netwm.icons[0].height);
             e_icon_alpha_set(o, 1);
             return o;
          }
     }
#endif
   if (!o)
     {
        if ((ec->desktop) && (ec->icon_preference != E_ICON_PREF_NETWM))
          {
             o = e_util_desktop_icon_add(ec->desktop, 64, evas);
             if (o)
               return o;
          }
#ifndef HAVE_WAYLAND_ONLY
        else if (ec->netwm.icons)
          {
             o = e_icon_add(evas);
             e_icon_data_set(o, ec->netwm.icons[0].data,
                             ec->netwm.icons[0].width,
                             ec->netwm.icons[0].height);
             e_icon_alpha_set(o, 1);
             return o;
          }
#endif
     }

   o = e_icon_add(evas);
   e_util_icon_theme_set(o, "unknown");
   return o;
}

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

E_API void
e_client_ping(E_Client *ec)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!e_config->ping_clients) return;
   ec->ping_ok = 0;
   evas_object_smart_callback_call(ec->frame, "ping", NULL);
   ec->ping = ecore_loop_time_get();
   if (ec->ping_poller) ecore_poller_del(ec->ping_poller);
   ec->ping_poller = ecore_poller_add(ECORE_POLLER_CORE,
                                      e_config->ping_clients_interval,
                                      _e_client_cb_ping_poller, ec);
}

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

E_API void
e_client_move_cancel(void)
{
   if (!ecmove) return;
   if (ecmove->cur_mouse_action)
     {
        E_Client *ec;

        ec = ecmove;
        e_object_ref(E_OBJECT(ec));
        _e_client_mouse_action_end(ec);
        e_object_unref(E_OBJECT(ec));
     }
   else
     _e_client_move_end(ecmove);
}

E_API void
e_client_resize_cancel(void)
{
   if (!ecresize) return;
   if (ecresize->cur_mouse_action)
     {
        E_Client *ec;

        ec = ecresize;
        e_object_ref(E_OBJECT(ec));
        _e_client_mouse_action_end(ec);
        e_object_unref(E_OBJECT(ec));
     }
   else
     _e_client_resize_end(ecresize);
}

E_API Eina_Bool
e_client_resize_begin(E_Client *ec)
{
   if ((ec->shaded) || (ec->shading) ||
       (ec->fullscreen) || (ec->lock_user_size))
     goto error;
   if (!_e_client_action_input_win_new()) goto error;
   if (!ec->lock_user_stacking)
     {
        if (e_config->border_raise_on_mouse_action)
          evas_object_raise(ec->frame);
     }
   ecresize = ec;
   _e_client_hook_call(E_CLIENT_HOOK_RESIZE_BEGIN, ec);
   if (!e_client_util_resizing_get(ec))
     {
        if (ecresize == ec) ecresize = NULL;
        _e_client_action_input_win_del();
        return EINA_FALSE;
     }
   E_FREE_FUNC(ec->raise_timer, ecore_timer_del);
   _e_client_action_event_grabber_init(ec);
   return EINA_TRUE;
error:
   ec->resize_mode = E_POINTER_RESIZE_NONE;
   return EINA_FALSE;
}


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

E_API void
e_client_frame_recalc(E_Client *ec)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (!ec->frame) return;
   evas_object_smart_callback_call(ec->frame, "frame_recalc", NULL);
}

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

E_API void
e_client_signal_move_begin(E_Client *ec, const char *sig, const char *src EINA_UNUSED)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;

   if (e_client_util_resizing_get(ec) || (ec->moving)) return;
   _e_client_moveinfo_gather(ec, sig);
   if (!_e_client_move_begin(ec)) return;
   _e_client_action_init(ec);
   e_pointer_mode_push(ec, E_POINTER_MOVE);
}

E_API void
e_client_signal_move_end(E_Client *ec, const char *sig EINA_UNUSED, const char *src EINA_UNUSED)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->zone) return;
   if (!ec->moving) return;
   _e_client_move_end(ec);
   e_zone_edge_enable();
   e_zone_flip_coords_handle(ec->zone, -1, -1);
   _e_client_action_finish();
}

E_API void
e_client_signal_resize_begin(E_Client *ec, const char *dir, const char *sig, const char *src EINA_UNUSED)
{
   int resize_mode = E_POINTER_RESIZE_BR;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   if (e_client_util_resizing_get(ec) || (ec->moving)) return;
   if (!strcmp(dir, "tl"))
     {
        resize_mode = E_POINTER_RESIZE_TL;
     }
   else if (!strcmp(dir, "t"))
     {
        resize_mode = E_POINTER_RESIZE_T;
     }
   else if (!strcmp(dir, "tr"))
     {
        resize_mode = E_POINTER_RESIZE_TR;
     }
   else if (!strcmp(dir, "r"))
     {
        resize_mode = E_POINTER_RESIZE_R;
     }
   else if (!strcmp(dir, "br"))
     {
        resize_mode = E_POINTER_RESIZE_BR;
     }
   else if (!strcmp(dir, "b"))
     {
        resize_mode = E_POINTER_RESIZE_B;
     }
   else if (!strcmp(dir, "bl"))
     {
        resize_mode = E_POINTER_RESIZE_BL;
     }
   else if (!strcmp(dir, "l"))
     {
        resize_mode = E_POINTER_RESIZE_L;
     }
   ec->resize_mode = resize_mode;
   _e_client_moveinfo_gather(ec, sig);
   if (!e_client_resize_begin(ec))
     return;
   _e_client_action_init(ec);
   e_pointer_mode_push(ec, ec->resize_mode);
}

E_API void
e_client_signal_resize_end(E_Client *ec, const char *dir EINA_UNUSED, const char *sig EINA_UNUSED, const char *src EINA_UNUSED)
{
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!e_client_util_resizing_get(ec)) return;
   _e_client_resize_handle(ec);
   _e_client_resize_end(ec);
   ec->changes.reset_gravity = 1;
   EC_CHANGED(ec);
   _e_client_action_finish();
}

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

E_API void
e_client_resize_limit(const E_Client *ec, int *w, int *h)
{
   int l = 0, r = 0, t = 0, b = 0;
   int dw = 0, dh = 0;
   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);

   e_comp_object_frame_geometry_get(ec->frame, &l, &r, &t, &b);
   if ((ec->frame) && e_comp_object_frame_allowed(ec->frame))
     {
        e_comp_object_frame_wh_unadjust(ec->frame, ec->w, ec->h, &dw, &dh);
        e_comp_object_frame_wh_unadjust(ec->frame, *w, *h, w, h);
     }
   else
     {
        *w -= l + r;
        *h -= t + b;
        dw = ec->w;
        dh = ec->h;
     }
   dw = abs(*w - dw);
   dh = abs(*h - dh);
   if (*h < 1) *h = 1;
   if (*w < 1) *w = 1;

   if ((ec->icccm.max_w > 0) && (*w > ec->icccm.max_w)) *w = ec->icccm.max_w;
   else if (*w < ec->icccm.min_w)
     *w = ec->icccm.min_w;
   if ((ec->icccm.max_h > 0) && (*h > ec->icccm.max_h)) *h = ec->icccm.max_h;
   else if (*h < ec->icccm.min_h)
     *h = ec->icccm.min_h;

   if (ec->icccm.step_w > 0)
     {
        int bw = ec->icccm.base_w ?: ec->icccm.min_w;

        bw = bw + (((*w - bw) / ec->icccm.step_w) * ec->icccm.step_w);
        if ((bw > ec->icccm.min_w) && ((ec->icccm.max_w < 1) || (bw < ec->icccm.max_w)))
          *w = bw;
     }
   if (ec->icccm.step_h > 0)
     {
        int bh = ec->icccm.base_h ?: ec->icccm.min_h;

        bh = bh + (((*h - bh) / ec->icccm.step_h) * ec->icccm.step_h);
        if ((bh > ec->icccm.min_h) && ((ec->icccm.max_h < 1) || (bh < ec->icccm.max_h)))
          *h = bh;
     }

   if (EINA_DBL_NONZERO(ec->icccm.min_aspect) ||
       EINA_DBL_NONZERO(ec->icccm.max_aspect))
     {
        double a = (double)*w / *h;
        Evas_Aspect_Control aspect;
        int aw, ah;
        double val;

        if (a < ec->icccm.min_aspect)
          {
             if (dw)
               aspect = EVAS_ASPECT_CONTROL_HORIZONTAL;
             else if (dh)
               aspect = EVAS_ASPECT_CONTROL_VERTICAL;
             else
               aspect = EVAS_ASPECT_CONTROL_BOTH;
             switch (aspect)
               {
                case EVAS_ASPECT_CONTROL_HORIZONTAL:
                  val = ((*h - (*w / ec->icccm.min_aspect)) * ec->icccm.step_h) / ec->icccm.step_h;
                  if (val > 0)
                    ah = ceil(val);
                  else
                    ah = floor(val);
                  if (*h - ah > ec->icccm.min_h)
                    {
                       *h -= ah;
                       break;
                    }
                  EINA_FALLTHROUGH;
                  /* No break */
                default:
                  val = (((*h * ec->icccm.min_aspect) - *w) * ec->icccm.step_w) / ec->icccm.step_w;
                  if (val > 0)
                    aw = ceil(val);
                  else
                    aw = floor(val);
                  if (*w + aw < ec->icccm.max_w)
                    *w += aw;
                  break;
               }
          }
        a = (double)*w / *h;
        if (a < ec->icccm.min_aspect) abort();
        if (a > ec->icccm.max_aspect)
          {
             if (dw)
               aspect = EVAS_ASPECT_CONTROL_HORIZONTAL;
             else if (dh)
               aspect = EVAS_ASPECT_CONTROL_VERTICAL;
             else
               aspect = EVAS_ASPECT_CONTROL_BOTH;
             switch (aspect)
               {
                case EVAS_ASPECT_CONTROL_HORIZONTAL:
                  val = (((*w / ec->icccm.max_aspect) - *h) * ec->icccm.step_h) / ec->icccm.step_h;
                  if (val > 0)
                    ah = ceil(val);
                  else
                    ah = floor(val);
                  if (*h + ah > ec->icccm.max_h)
                    {
                       *h += ah;
                       break;
                    }
                  EINA_FALLTHROUGH;
                  /* No break */
                default:
                  val = ((*w - (*h * ec->icccm.max_aspect)) * ec->icccm.step_w) / ec->icccm.step_w;
                  if (val > 0)
                    aw = ceil(val);
                  else
                    aw = floor(val);
                  if (*w - aw > ec->icccm.min_w)
                    *w -= aw;
                  break;
               }
          }
     }

   if (*h < 1) *h = 1;
   if (*w < 1) *w = 1;

   if ((ec->frame) && e_comp_object_frame_allowed(ec->frame))
     {
        if (ec->frame)
          e_comp_object_frame_wh_adjust(ec->frame, *w, *h, w, h);
     }
   else
     {
        *w += l + r;
        *h += t + b;
     }
}

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



E_API E_Client *
e_client_under_pointer_get(E_Desk *desk, E_Client *exclude)
{
   int x, y;

   /* We need to ensure that we can get the comp window for the
    * zone of either the given desk or the desk of the excluded
    * window, so return if neither is given */
   if (desk)
     ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   else if (exclude)
     ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   else
     return NULL;

   if (!desk)
     {
        desk = exclude->desk;
        if (!desk)
          {
             if (exclude->zone)
               desk = e_desk_current_get(exclude->zone);
             else
               desk = e_desk_current_get(e_zone_current_get());
          }
     }

   return _e_client_under_pointer_helper(desk, exclude, x, y);
}

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

E_API int
e_client_pointer_warp_to_center_now(E_Client *ec)
{
   if (e_config->disable_all_pointer_warps) return 0;
   if (warp_client == ec)
     {
        ecore_evas_pointer_warp(e_comp->ee, warp_to_x, warp_to_y);
        warp_to = 0;
        _e_client_pointer_warp_to_center_timer(NULL);
     }
   else
     {
        if (e_client_pointer_warp_to_center(ec))
          e_client_pointer_warp_to_center_now(ec);
     }
   return 1;
}

E_API int
e_client_pointer_warp_to_center(E_Client *ec)
{
   int x, y;
   E_Client *cec = NULL;

   ec = e_client_stack_active_adjust(ec);
   if (ec->override) return 0;
   if (!ec->zone) return 0;
   if (e_config->disable_all_pointer_warps) return 0;
   /* Only warp the pointer if it is not already in the area of
    * the given border */
   ecore_evas_pointer_xy_get(e_comp->ee, &x, &y);
   if ((x >= ec->x) && (x <= (ec->x + ec->w)) &&
       (y >= ec->y) && (y <= (ec->y + ec->h)))
     {
        cec = _e_client_under_pointer_helper(ec->desk, ec, x, y);
        if (cec) cec = e_client_stack_active_adjust(cec);
        if (cec == ec) return 0;
     }

   warp_to_x = ec->x + (ec->w / 2);
   if (warp_to_x < (ec->zone->x + 1))
     warp_to_x = ec->zone->x + ((ec->x + ec->w - ec->zone->x) / 2);
   else if (warp_to_x > (ec->zone->x + ec->zone->w))
     warp_to_x = (ec->zone->x + ec->zone->w + ec->x) / 2;

   warp_to_y = ec->y + (ec->h / 2);
   if (warp_to_y < (ec->zone->y + 1))
     warp_to_y = ec->zone->y + ((ec->y + ec->h - ec->zone->y) / 2);
   else if (warp_to_y > (ec->zone->y + ec->zone->h))
     warp_to_y = (ec->zone->y + ec->zone->h + ec->y) / 2;

   /* TODO: handle case where another border is over the exact center,
    * find a place where the requested border is not overlapped?
    *
   if (!cec) cec = _e_client_under_pointer_helper(ec->desk, ec, x, y);
   if (cec != ec)
     {
     }
   */

   warp_to = 1;
   warp_client = ec;
   ecore_evas_pointer_xy_get(e_comp->ee, &warp_x[0], &warp_y[0]);
   if (warp_timer) ecore_timer_del(warp_timer);
   warp_timer = ecore_timer_loop_add(0.01, _e_client_pointer_warp_to_center_timer, ec);
   return 1;
}

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

E_API void
e_client_redirected_set(E_Client *ec, Eina_Bool set)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   if (ec->input_only) return;
   set = !!set;
   if (ec->redirected == set) return;
   if (set)
     {
        e_client_frame_recalc(ec);
        if (!_e_client_hook_call(E_CLIENT_HOOK_REDIRECT, ec)) return;
     }
   else
     {
        if (!_e_client_hook_call(E_CLIENT_HOOK_UNREDIRECT, ec)) return;
     }
   e_comp_object_redirected_set(ec->frame, set);
   ec->redirected = !!set;
}

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

E_API void
e_client_next_mouse_action_ignore(E_Client *ec)
{
   EINA_SAFETY_ON_NULL_RETURN(ec);
   ec->next_mouse_action_ignore = EINA_TRUE;
}

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

E_API Eina_Bool
e_client_is_stacking(const E_Client *ec)
{
   return e_comp->layers[e_comp_canvas_layer_map(ec->layer)].obj == ec->frame;
}

E_API Eina_Bool
e_client_has_xwindow(const E_Client *ec)
{
#ifdef HAVE_WAYLAND_ONLY
   (void)ec;
   return EINA_FALSE;
#else
# ifdef HAVE_WAYLAND
   if (!e_pixmap_is_x(ec->pixmap))
     return !!e_comp_wl_client_xwayland_pixmap(ec);
# endif
   return e_pixmap_is_x(ec->pixmap);
#endif
}

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

E_API void
e_client_layout_cb_set(E_Client_Layout_Cb cb)
{
   if (_e_client_layout_cb && cb)
     CRI("ATTEMPTING TO OVERWRITE EXISTING CLIENT LAYOUT HOOK!!!");
   _e_client_layout_cb = cb;
}

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

E_API void
e_client_parent_set(E_Client *ec, E_Client *parent)
{
//   E_Client *prev;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (parent)
     {
        E_OBJECT_CHECK(parent);
        E_OBJECT_TYPE_CHECK(parent, E_CLIENT_TYPE);
     }

   if (ec == parent)
     {
        ERR("refusing to set client as its own parent");
        return;
     }

   if (parent && (parent->parent == ec))
     {
        ERR("refusing to set client as its parent's parent");
        return;
     }

   if (ec->parent == parent) return;

   /* If we already have a parent, remove it */
   if (ec->parent)
     {
        ec->parent->transients = eina_list_remove(ec->parent->transients, ec);
        if (ec->parent->modal == ec) ec->parent->modal = NULL;
        ec->parent = NULL;
     }
   if (parent)
     {
//        prev = eina_list_last_data_get(parent->transients);
        parent->transients = eina_list_append(parent->transients, ec);
        ec->parent = parent;
     }
   if (ec->parent)
     {
        evas_object_layer_set(ec->frame, ec->parent->layer);

/* complex stacking right above one dialog after the other hurts the
 * simple assumptions that the window should be on top by clients so
 * be dumber and disable this
        if (prev)
          evas_object_stack_above(ec->frame, prev->frame);
        else
          evas_object_stack_above(ec->frame, parent->frame);
 */
        evas_object_raise(ec->frame);

        if (e_client_util_ignored_get(ec)) return;
        if (e_pixmap_usable_get(ec->pixmap) && (!ec->lock_user_location))
          e_comp_object_util_center_on(ec->frame, parent->frame);

        if ((e_config->focus_setting == E_FOCUS_NEW_DIALOG) ||
            (ec->parent->focused && (e_config->focus_setting == E_FOCUS_NEW_DIALOG_IF_OWNER_FOCUSED)))
          ec->take_focus = 1;
     }
}

static void
_e_client_stack_top(E_Client *ec_base EINA_UNUSED,
                    E_Client *ec_stack, E_Client *ec_above)
{
/* complex stacking right above one dialog after the other hurts the
 * simple assumptions that the window should be on top by clients so
 * be dumber and disable this
   if (!ec_above)
     {
        evas_object_raise(ec_stack->frame);
        evas_object_stack_above(ec_base->frame, ec_stack->frame);
     }
   else
     {
        if (ec_above->transients)
          {
             e_client_transients_restack(ec_above);
             E_Client *ec_last = eina_list_last_data_get(ec_above->transients);
             if (ec_last)
               evas_object_stack_above(ec_stack->frame, ec_last->frame);
             else
               evas_object_stack_above(ec_stack->frame, ec_above->frame);
          }
        else
          evas_object_stack_above(ec_stack->frame, ec_above->frame);
     }
 */
   if (!ec_above) evas_object_raise(ec_stack->frame);
   else
     {
        if (ec_above->transients) e_client_transients_restack(ec_above);
        evas_object_raise(ec_stack->frame);
     }
}

E_API void
e_client_transients_restack(E_Client *ec)
{
   E_Client *child, *below = NULL;
   Eina_List *list;

   E_OBJECT_CHECK(ec);
   E_OBJECT_TYPE_CHECK(ec, E_CLIENT_TYPE);
   if (!ec->transients) return;

   list = eina_list_clone(ec->transients);
   EINA_LIST_FREE(list, child)
     {
        // Don't stack iconic transients. If the user wants these shown,
        // that's another option.
        if (child->iconic) continue;
        _e_client_stack_top(ec, child, below);
        below = child;
     }
}
