#if (ECORE_VERSION_MAJOR > 1) || (ECORE_VERSION_MINOR >= 8)

#include "e.h"
#include "e_mod_main.h"
#define HISTORY_MAX 8
#define DEBUG_INFO 1

static E_Config_DD *conf_edd = NULL;
Config *access_config = NULL;

typedef struct
{
   E_Zone         *zone;
   Ecore_X_Window  win;
   Ecore_Timer    *timer;
   Ecore_Timer    *double_down_timer;
   Ecore_Timer    *tap_timer;
   Evas_Object    *info;
   Evas_Object    *text;
   int             x, y, dx, dy, mx, my;
   int             mouse_history[HISTORY_MAX];
   unsigned int    dt;
   Eina_Inlist    *history;
   Eina_Bool       longpressed : 1;
   Eina_Bool       two_finger_down : 1;
   Eina_Bool       double_down : 1;
} Cover;

#if DEBUG_INFO
  static Ecore_Timer    *dbg_timer = NULL;
  static Eina_Bool
  _reset_text(void *data)
  {
     Cover *cov = data;
     if(!cov) return EINA_FALSE;

     ecore_timer_del(dbg_timer);
     dbg_timer = NULL;
     evas_object_text_text_set(cov->text, "Screen Reader Mode");
     return EINA_FALSE;
  }
  #define INFO(cov, txt) \
    evas_object_text_text_set(cov->text, txt); \
    EINA_LOG_INFO("%s", txt); \
    if (dbg_timer) \
      { \
         ecore_timer_del(dbg_timer); \
         dbg_timer = NULL; \
      } \
     dbg_timer = ecore_timer_add(1.0, _reset_text, cov);
#else
  #define INFO(cov, txt) EINA_LOG_INFO("%s", txt)
#endif

typedef struct
{
   EINA_INLIST;
   int             device;
} Multi;

static E_Border *_prev_bd;

static Ecore_X_Atom _atom_access = 0;
static Ecore_X_Window target_win = 0;

static Eina_List *covers = NULL;
static Eina_List *handlers = NULL;
static Ecore_Event_Handler *client_message_handler = NULL;
static Ecore_Event_Handler *property_handler = NULL;
static int multi_device[3];

static void
_mouse_in_win_get(Cover *cov, int x, int y)
{
   Eina_List *l;
   Ecore_X_Window *skip;
   Cover *cov2;
   int i;

   skip = alloca(sizeof(Ecore_X_Window) * eina_list_count(covers));
   i = 0;
   EINA_LIST_FOREACH(covers, l, cov2)
     {
        skip[i] = cov2->win;
        i++;
     }

   /* TODO: if target window is changed without highlight object,
      then previous target window which has the highlight object
      should get the message. how? */
   target_win = ecore_x_window_shadow_tree_at_xy_with_skip_get
     (cov->zone->container->manager->root, x, y, skip, i);
}

static unsigned int
_win_angle_get(Ecore_X_Window win)
{
   Ecore_X_Window root;

   if (!win) return 0;

   int ret;
   int count;
   int angle = 0;
   unsigned char *prop_data = NULL;

   root = ecore_x_window_root_get(win);
   ret = ecore_x_window_prop_property_get(root,
       ECORE_X_ATOM_E_ILLUME_ROTATE_ROOT_ANGLE,
                         ECORE_X_ATOM_CARDINAL,
                        32, &prop_data, &count);

   if (ret && prop_data)
      memcpy (&angle, prop_data, sizeof (int));

   if (prop_data) free (prop_data);

   return angle;
}

static void
_coordinate_calibrate(Ecore_X_Window win, int *x, int *y)
{
   int tx, ty, w, h;
   unsigned int angle;

   if (!x) return;
   if (!y) return;

   angle = _win_angle_get(win);
   ecore_x_window_geometry_get(win, NULL, NULL, &w, &h);

   tx = *x;
   ty = *y;

   switch (angle)
     {
      case 90:
        *x = h - ty;
        *y = tx;
        break;

      case 180:
        *x = w - tx;
        *y = h - ty;
        break;

      case 270:
        *x = ty;
        *y = w - tx;
        break;

      default:
        break;
     }
}

static void
_mouse_win_fake_tap(Cover *cov, Ecore_Event_Mouse_Button *ev)
{
   int x, y;

   /* find target window to send message */
   _mouse_in_win_get(cov, ev->root.x, ev->root.y);

   ecore_x_pointer_xy_get(target_win, &x, &y);
   ecore_x_mouse_in_send(target_win, x, y);
   ecore_x_mouse_move_send(target_win, x, y);
   ecore_x_mouse_down_send(target_win, x, y, 1);
   ecore_x_mouse_up_send(target_win, x, y, 1);
   ecore_x_mouse_out_send(target_win, x, y);
}

static void
_messsage_read_send(Cover *cov)
{
   int x, y;

   /* find target window to send message */
   _mouse_in_win_get(cov, cov->x, cov->y);

   ecore_x_pointer_xy_get(target_win, &x, &y);
   _coordinate_calibrate(target_win, &x, &y);

   ecore_x_client_message32_send(target_win, ECORE_X_ATOM_E_ILLUME_ACCESS_CONTROL,
                                 ECORE_X_EVENT_MASK_WINDOW_CONFIGURE,
                                 target_win,
                                 ECORE_X_ATOM_E_ILLUME_ACCESS_ACTION_READ,
                                 x, y, 0);

#if DEBUG_INFO
   Eina_List *l;
   Cover *ecov;
   Eina_Strbuf *buf;

   buf = eina_strbuf_new();
   eina_strbuf_append_printf(buf, "read x:%d, y:%d", x, y);

   EINA_LIST_FOREACH(covers, l, ecov)
     {
       INFO(ecov, eina_strbuf_string_get(buf));
     }
   eina_strbuf_free(buf);
#endif
}

static Eina_Bool
_mouse_longpress(void *data)
{
   Cover *cov = data;
   int distance = 40;
   int dx, dy;

   cov->timer = NULL;
   dx = cov->x - cov->dx;
   dy = cov->y - cov->dy;
   if (((dx * dx) + (dy * dy)) < (distance * distance))
     {
        cov->longpressed = EINA_TRUE;
        INFO(cov, "longpress");

        if (!cov->double_down) _messsage_read_send(cov);
        else
          {
             INFO(cov, "double down and longpress");
             //TODO: send message to notify start longpress
          }
     }
   return EINA_FALSE;
}

static Eina_Bool
_mouse_double_down(void *data)
{
   Cover *cov = data;
   ecore_timer_del(cov->double_down_timer);
   cov->double_down_timer = NULL;
   return EINA_FALSE;
}

static void
_double_down_timeout(Cover *cov)
{
   double long_time = 0.5;
   double short_time = 0.3;
   int distance = 40;
   int dx, dy;

   dx = cov->x - cov->dx;
   dy = cov->y - cov->dy;

   if ((cov->double_down_timer) &&
       (((dx * dx) + (dy * dy)) < (distance * distance)))
     {
        // start double tap and move from here
        cov->double_down = EINA_TRUE;

        if (cov->timer)
          {
             ecore_timer_del(cov->timer);
             cov->timer = NULL;
          }
        /* check longpress after double down */
        cov->timer = ecore_timer_add(long_time, _mouse_longpress, cov);
     }

   if (cov->double_down_timer)
     {
        ecore_timer_del(cov->double_down_timer);
        cov->double_down_timer = NULL;
        return;
     }

   cov->double_down_timer = ecore_timer_add(short_time, _mouse_double_down, cov);
}

static Eina_Bool
_mouse_tap(void *data)
{
   Cover *cov = data;
   cov->tap_timer = NULL;

   _messsage_read_send(cov);

   return EINA_FALSE;
}

static void
_mouse_down(Cover *cov, Ecore_Event_Mouse_Button *ev)
{
   double long_time = 0.5;

   cov->dx = ev->x;
   cov->dy = ev->y;
   cov->mx = ev->x;
   cov->my = ev->y;
   cov->x = ev->x;
   cov->y = ev->y;
   cov->dt = ev->timestamp;
   cov->longpressed = EINA_FALSE;
   cov->timer = ecore_timer_add(long_time, _mouse_longpress, cov);

   if (cov->tap_timer)
     {
        ecore_timer_del(cov->tap_timer);
        cov->tap_timer = NULL;
     }

   /* check mouse double down - not two fingers, refer to double click */
   _double_down_timeout(cov);
}

static void
_mouse_up(Cover *cov, Ecore_Event_Mouse_Button *ev)
{
   double timeout = 0.15;
   double double_tap_timeout = 0.25;
   int distance = 40;
   int dx, dy;
   int x, y;
   int angle = 0;

   // for two finger panning
   if (cov->two_finger_down)
     {
        ecore_x_pointer_xy_get(target_win, &x, &y);
        ecore_x_mouse_up_send(target_win, x, y, 1);
        cov->two_finger_down = EINA_FALSE;
        ecore_x_mouse_out_send(target_win, x, y);
     }

   // reset double down and moving
   cov->double_down = EINA_FALSE;

   if (cov->timer)
     {
        ecore_timer_del(cov->timer);
        cov->timer = NULL;
     }

   if (cov->longpressed)
     {
        cov->longpressed = EINA_FALSE;
        return;
     }

   dx = ev->x - cov->dx;
   dy = ev->y - cov->dy;
   if (((dx * dx) + (dy * dy)) < (distance * distance))
     {
        if (ev->double_click)
          {
             /* activate message would change focused window
                FIXME: but it is possibe to create unfocused window
                in this case, the message should go to unfocused window? */
             _prev_bd = e_border_focused_get();

             INFO(cov, "double_click");
             ecore_x_e_illume_access_action_activate_send(target_win);
          }
        else if ((ev->timestamp - cov->dt) <= (timeout * 1000))
          {
             cov->tap_timer = ecore_timer_add(double_tap_timeout,
                                         _mouse_tap, cov);
          }
        else if ((ev->timestamp - cov->dt) > (timeout * 1000) &&
                 (ev->timestamp - cov->dt) < (2 * timeout * 1000))
          {
             INFO(cov, "tap");
             _mouse_win_fake_tap(cov, ev);
          }
     }
   else if (((dx * dx) + (dy * dy)) > (4 * distance * distance)
            && ((ev->timestamp - cov->dt) < (timeout * 1000)))
     {
        /* get root window rotation */
        angle = _win_angle_get(target_win);

        if (abs(dx) > abs(dy)) // left or right
          {
             if (dx > 0) // right
               {
                  INFO(cov, "single flick right");
                  switch (angle)
                    {
                     case 180:
                     case 270:
                       ecore_x_e_illume_access_action_read_prev_send
                                                        (target_win);
                     break;

                     case 90:
                     default:
                       ecore_x_e_illume_access_action_read_next_send
                                                        (target_win);
                     break;
                    }

               }
             else // left
               {
                  INFO(cov, "single flick left");
                  switch (angle)
                    {
                     case 180:
                     case 270:
                       ecore_x_e_illume_access_action_read_next_send
                                                        (target_win);
                     break;

                     case 90:
                     default:
                       ecore_x_e_illume_access_action_read_prev_send
                                                        (target_win);
                     break;
                    }
               }
          }
        else // up or down
          {
             if (dy > 0) // down
               {
                  INFO(cov, "single flick down");
                  switch (angle)
                    {
                     case 90:
                     case 180:
                       ecore_x_e_illume_access_action_read_prev_send
                                                        (target_win);
                     break;

                     case 270:
                     default:
                       ecore_x_e_illume_access_action_read_next_send
                                                        (target_win);
                     break;
                    }
               }
             else // up
               {
                  INFO(cov, "single flick up");
                  switch (angle)
                    {
                     case 90:
                     case 180:
                       ecore_x_e_illume_access_action_read_next_send
                                                        (target_win);
                     break;

                     case 270:
                     default:
                       ecore_x_e_illume_access_action_read_prev_send
                                                        (target_win);
                     break;
                    }
               }
          }
     }
   cov->longpressed = EINA_FALSE;
}

static void
_mouse_move(Cover *cov, Ecore_Event_Mouse_Move *ev)
{
   int x, y;

   //FIXME: why here.. after long press you cannot go below..
   //if (!cov->down) return;

   //FIXME: one finger cannot come here
   //_record_mouse_history(cov, ev);

   ecore_x_pointer_xy_get(target_win, &x, &y);
   ecore_x_mouse_move_send(target_win, x, y);
}

static void
_mouse_wheel(Cover *cov __UNUSED__, Ecore_Event_Mouse_Wheel *ev __UNUSED__)
{
   if (ev->z == -1) // up
     {
#if ECORE_VERSION_MAJOR >= 1
# if ECORE_VERSION_MINOR >= 8
        ecore_x_e_illume_access_action_up_send(target_win);
# endif
#endif
     }
   else if (ev->z == 1) // down
     {
#if ECORE_VERSION_MAJOR >= 1
# if ECORE_VERSION_MINOR >= 8
        ecore_x_e_illume_access_action_down_send(target_win);
# endif
#endif
     }
}

static Eina_Bool
_cb_mouse_down(void    *data __UNUSED__,
               int      type __UNUSED__,
               void    *event)
{
   Ecore_Event_Mouse_Button *ev = event;
   Eina_List *l;
   Cover *cov;
   int i = 0;
   int x, y;
   E_Border *bd;

   for (i = 0; i < 3; i++)
     {
        if (multi_device[i] == -1)
          {
             multi_device[i] = ev->multi.device;
             break;
          }
        else if (multi_device[i] == ev->multi.device) break;
     }

   /* activate message would change focused window
      FIXME: but it is possibe to create unfocused window
      in this case, the message should go to focused window? */
   bd = e_border_focused_get();
   if (bd && (bd != _prev_bd)) target_win = bd->client.win;

   EINA_LIST_FOREACH(covers, l, cov)
     {
        if (ev->window == cov->win)
          {
             // XXX change specific number
             if (ev->multi.device == multi_device[0])
               _mouse_down(cov, ev);

             if (ev->multi.device == multi_device[1] && !(cov->two_finger_down))
               {
                  // prevent longpress client message by two finger
                  if (cov->timer)
                    {
                       ecore_timer_del(cov->timer);
                       cov->timer = NULL;
                    }

                  ecore_x_pointer_xy_get(target_win, &x, &y);

                  ecore_x_mouse_in_send(target_win, x, y);
                  ecore_x_mouse_move_send(target_win, x, y);
                  ecore_x_mouse_down_send(target_win, x, y, 1);

                  cov->two_finger_down = EINA_TRUE;
               }
             return ECORE_CALLBACK_PASS_ON;
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_mouse_up(void    *data __UNUSED__,
             int      type __UNUSED__,
             void    *event)
{
   Ecore_Event_Mouse_Button *ev = event;
   Eina_List *l;
   Cover *cov;

   EINA_LIST_FOREACH(covers, l, cov)
     {
        if (ev->window == cov->win)
          {
             if (ev->buttons == 1)
               _mouse_up(cov, ev);
             return ECORE_CALLBACK_PASS_ON;
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_mouse_move(void    *data __UNUSED__,
               int      type __UNUSED__,
               void    *event)
{
   Ecore_Event_Mouse_Move *ev = event;
   Eina_List *l;
   Cover *cov;
   int angle = 0;

   EINA_LIST_FOREACH(covers, l, cov)
     {
        cov->x = ev->x;
        cov->y = ev->y;

        if (ev->window == cov->win)
          {
             //if (ev->multi.device == multi_device[0] || ev->multi.device == multi_device[1])
             if (cov->two_finger_down && ev->multi.device == multi_device[1])
               _mouse_move(cov, ev);
             else if (cov->longpressed && // client message for moving is available only after long press is detected
                      !(cov->double_down) && /* mouse move after double down should not send read message */
                      !(cov->two_finger_down) && ev->multi.device == multi_device[0])
               {
                  INFO(cov, "read");
                  _messsage_read_send(cov);
               }
             else if (cov->double_down && // client message for moving is available only after long press is detected
                      !(cov->two_finger_down) && ev->multi.device == multi_device[0])
               {
                  int distance = 5;
                  int dx, dy;

                  if (cov->longpressed)
                    {
                       //TODO: send message to notify move afte longpress
                    }
                  else
                    {
                       dx = ev->x - cov->mx;
                       dy = ev->y - cov->my;

                       /* get root window rotation */
                       angle = _win_angle_get(target_win);

                       if (((dx * dx) + (dy * dy)) > (distance * distance))
                         {

                            if (abs(dx) > abs(dy)) // left or right
                              {
                                 if (dx > 0) // right
                                   {
                                      INFO(cov, "mouse double down and move - right");
                                      switch (angle)
                                        {
                                         case 180:
                                         case 270:
                                           ecore_x_e_illume_access_action_down_send(target_win);
                                         break;

                                         case 90:
                                         default:
                                           ecore_x_e_illume_access_action_up_send(target_win);
                                         break;
                                        }
                                   }
                                 else // left
                                   {
                                      INFO(cov, "mouse double down and move - left");
                                      switch (angle)
                                        {
                                         case 180:
                                         case 270:
                                           ecore_x_e_illume_access_action_up_send(target_win);
                                         break;

                                         case 90:
                                         default:
                                           ecore_x_e_illume_access_action_down_send(target_win);
                                         break;
                                        }
                                   }
                              }
                            else // up or down
                              {
                                 if (dy > 0) // down
                                   {
                                      INFO(cov, "mouse double down and move - down");
                                      switch (angle)
                                        {
                                         case 90:
                                         case 180:
                                           ecore_x_e_illume_access_action_up_send(target_win);
                                         break;

                                         case 270:
                                         default:
                                           ecore_x_e_illume_access_action_down_send(target_win);
                                         break;
                                        }
                                   }
                                 else // up
                                   {
                                      INFO(cov, "mouse double down and move - up");
                                      switch (angle)
                                        {
                                         case 90:
                                         case 180:
                                           ecore_x_e_illume_access_action_down_send(target_win);
                                         break;

                                         case 270:
                                         default:
                                           ecore_x_e_illume_access_action_up_send(target_win);
                                         break;
                                        }
                                   }
                               }

                            cov->mx = ev->x;
                            cov->my = ev->y;
                         }
                    }
               }

             return ECORE_CALLBACK_PASS_ON;
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_mouse_wheel(void    *data __UNUSED__,
                int      type __UNUSED__,
                void    *event)
{
   Ecore_Event_Mouse_Wheel *ev = event;
   Eina_List *l;
   Cover *cov;

   EINA_LIST_FOREACH(covers, l, cov)
     {
        if (ev->window == cov->win)
          {
             _mouse_wheel(cov, ev);
             return ECORE_CALLBACK_PASS_ON;
          }
     }
   return ECORE_CALLBACK_PASS_ON;
}

static Cover *
_cover_new(E_Zone *zone)
{
   Cover *cov;

   cov = E_NEW(Cover, 1);
   if (!cov) return NULL;
   cov->zone = zone;

#if DEBUG_INFO
   Ecore_Evas *ee;
   ee = ecore_evas_new(NULL,
                       zone->container->x + zone->x,
                       zone->container->y + zone->y,
                       zone->w, zone->h,
                       NULL);
   ecore_evas_alpha_set(ee, EINA_TRUE);
   cov->win = (Ecore_X_Window)ecore_evas_window_get(ee);

   /* create infomation */
   Evas *e;
   e = ecore_evas_get(ee);
   cov->info = evas_object_rectangle_add(e);
   evas_object_color_set(cov->info, 255, 255, 255, 100);
   evas_object_move(cov->info, zone->container->x + zone->x, zone->container->y + zone->y);
   evas_object_resize(cov->info, zone->w, 30);
   evas_object_show(cov->info);

   cov->text = evas_object_text_add(e);
   evas_object_text_style_set(cov->text, EVAS_TEXT_STYLE_PLAIN);
   evas_object_text_font_set(cov->text, "DejaVu", 14);
   INFO(cov, "Screen Reader Mode");

   evas_object_color_set(cov->text, 0, 0, 0, 255);
   evas_object_resize(cov->text, (zone->w / 8), 20);
   evas_object_move(cov->text, zone->container->x + zone->x + 5, zone->container->y + zone->y + 5);
   evas_object_show(cov->text);

#else
   cov->win = ecore_x_window_input_new(zone->container->manager->root,
                                       zone->container->x + zone->x,
                                       zone->container->y + zone->y,
                                       zone->w, zone->h);
#endif

   ecore_x_input_multi_select(cov->win);

   ecore_x_icccm_title_set(cov->win, "access-screen-reader");
   ecore_x_netwm_name_set(cov->win, "access-screen-reader");

   ecore_x_window_ignore_set(cov->win, 1);
   ecore_x_window_configure(cov->win,
                            ECORE_X_WINDOW_CONFIGURE_MASK_SIBLING |
                            ECORE_X_WINDOW_CONFIGURE_MASK_STACK_MODE,
                            0, 0, 0, 0, 0,
                            zone->container->layers[8].win,
                            ECORE_X_WINDOW_STACK_ABOVE);
   ecore_x_window_show(cov->win);
   ecore_x_window_raise(cov->win);

   return cov;
}

static void
_covers_init(void)
{
   Eina_List *l, *l2, *l3;
   E_Manager *man;
   int i = 0;

   EINA_LIST_FOREACH(e_manager_list(), l, man)
     {
        E_Container *con;
        EINA_LIST_FOREACH(man->containers, l2, con)
          {
             E_Zone *zone;
             EINA_LIST_FOREACH(con->zones, l3, zone)
               {
                  Cover *cov = _cover_new(zone);
                  if (cov)
                    {
                       covers = eina_list_append(covers, cov);
                       for (i = 0; i < HISTORY_MAX; i++) cov->mouse_history[i] = -1;
                    }
               }
          }
     }
}

static void
_covers_shutdown(void)
{
   Cover *cov;

   EINA_LIST_FREE(covers, cov)
     {
        ecore_x_window_ignore_set(cov->win, 0);
        ecore_x_window_free(cov->win);
        evas_object_del(cov->info);
        evas_object_del(cov->text);

        if (cov->timer)
          {
             ecore_timer_del(cov->timer);
             cov->timer = NULL;
          }

        if (cov->double_down_timer)
          {
             ecore_timer_del(cov->double_down_timer);
             cov->double_down_timer = NULL;
          }

        if (cov->tap_timer)
          {
             ecore_timer_del(cov->tap_timer);
             cov->tap_timer = NULL;
          }

#if DEBUG_INFO
        if (dbg_timer)
          {
             ecore_timer_del(dbg_timer);
             dbg_timer = NULL;
          }
#endif

        free(cov);
     }
}

static Eina_Bool
_cb_zone_add(void    *data __UNUSED__,
             int      type __UNUSED__,
             void    *event __UNUSED__)
{
   _covers_shutdown();
   _covers_init();
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_zone_del(void    *data __UNUSED__,
             int      type __UNUSED__,
             void    *event __UNUSED__)
{
   _covers_shutdown();
   _covers_init();
   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_zone_move_resize(void    *data __UNUSED__,
                     int      type __UNUSED__,
                     void    *event __UNUSED__)
{
   _covers_shutdown();
   _covers_init();
   return ECORE_CALLBACK_PASS_ON;
}

static void
_events_init(void)
{
   int i = 0;

   handlers = eina_list_append
     (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_DOWN,
                                        _cb_mouse_down, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP,
                                        _cb_mouse_up, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_MOVE,
                                        _cb_mouse_move, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(ECORE_EVENT_MOUSE_WHEEL,
                                        _cb_mouse_wheel, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(E_EVENT_ZONE_ADD,
                                        _cb_zone_add, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(E_EVENT_ZONE_DEL,
                                        _cb_zone_del, NULL));
   handlers = eina_list_append
     (handlers, ecore_event_handler_add(E_EVENT_ZONE_MOVE_RESIZE,
                                        _cb_zone_move_resize, NULL));

   for (i = 0; i < 3; i++) multi_device[i] = -1;
}

static void
_events_shutdown(void)
{
   E_FREE_LIST(handlers, ecore_event_handler_del);
}

static Eina_Bool
_cb_property_change(void *data __UNUSED__,
                   int   type __UNUSED__,
                   void *ev)
{
   E_Border *bd;
   Ecore_X_Event_Window_Property *event = ev;

   if (event->atom == ECORE_X_ATOM_NET_ACTIVE_WINDOW)
     {
        bd = e_border_focused_get();
        if (bd) target_win = bd->client.win;
	 }

   return ECORE_CALLBACK_PASS_ON;
}

static Eina_Bool
_cb_client_message(void *data __UNUSED__,
                   int   type __UNUSED__,
                   void *ev)
{
   int block;
   Ecore_X_Event_Client_Message *event = ev;

   if (event->message_type != _atom_access)
     return ECORE_CALLBACK_PASS_ON;

   block = e_config_save_block_get();
   if (block) e_config_save_block_set(!block);

   if ((Eina_Bool)event->data.l[0])
     {
        EINA_LOG_INFO("[access module] module enable");
        _covers_init();
        _events_init();
        access_config->window = EINA_TRUE;
     }
   else
     {
        EINA_LOG_INFO("[access module] module disable");
        _covers_shutdown();
        _events_shutdown();
        access_config->window = EINA_FALSE;
     }

   /* save config value */
   e_config_domain_save("module.access", conf_edd, access_config);
   e_config_save_block_set(block);

   return ECORE_CALLBACK_PASS_ON;
}

/***************************************************************************/
/* module setup */
EAPI E_Module_Api e_modapi =
{
   E_MODULE_API_VERSION, "Access"
};

EAPI void *
e_modapi_init(E_Module *m)
{
   if (!_atom_access)
     _atom_access = ecore_x_atom_get("_E_MOD_ACC_SCR_READER_");

   ecore_x_event_mask_set(ecore_x_window_root_first_get(),
                          ECORE_X_EVENT_MASK_WINDOW_CONFIGURE);
   client_message_handler = ecore_event_handler_add
                             (ECORE_X_EVENT_CLIENT_MESSAGE, _cb_client_message, NULL);
   ecore_x_event_mask_set(ecore_x_window_root_first_get(),
                          ECORE_X_EVENT_MASK_WINDOW_PROPERTY);
   property_handler = ecore_event_handler_add
                       (ECORE_X_EVENT_WINDOW_PROPERTY, _cb_property_change, NULL);

   /* load config value */
   conf_edd = E_CONFIG_DD_NEW("Access_Config", Config);
   E_CONFIG_VAL(conf_edd, Config, window, UCHAR);

   access_config = e_config_domain_load("module.access", conf_edd);

   if (!access_config)
     {
        access_config = E_NEW(Config, 1);
        access_config->window = EINA_FALSE;
        return m;
     }

   if (access_config->window)
     {
        _covers_shutdown();
        _covers_init();
        _events_init();
     }
   else
     {
        _covers_shutdown();
        _events_shutdown();
     }

   return m;
}

EAPI int
e_modapi_shutdown(E_Module *m __UNUSED__)
{
   EINA_LOG_INFO("[access module] module shutdown");
   if (client_message_handler) ecore_event_handler_del(client_message_handler);
   if (property_handler) ecore_event_handler_del(property_handler);

   _covers_shutdown();
   _events_shutdown();

   return 1;
}

EAPI int
e_modapi_save(E_Module *m __UNUSED__)
{
   e_config_domain_save("module.access", conf_edd, access_config);
   return 1;
}

#endif
