#ifdef HAVE_CONFIG_H
# include <config.h>
#endif /* ifdef HAVE_CONFIG_H */

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

#include <langinfo.h>

#include "Ecore.h"
#include "ecore_private.h"
#include "ecore_x_private.h"
#include "Ecore_X.h"
#include "Ecore_X_Atoms.h"

/** OpenBSD does not define CODESET
 * FIXME ??
 */

#ifndef CODESET
#define CODESET "INVALID"
#endif /* ifndef CODESET */

typedef struct _Ecore_X_Mouse_Down_Info
{
   EINA_INLIST;
   int       dev;
   Window    last_win;
   Window    last_last_win;
   Window    last_event_win;
   Window    last_last_event_win;
   Time      last_time;
   Time      last_last_time;
   Eina_Bool did_double : 1;
   Eina_Bool did_triple : 1;
} Ecore_X_Mouse_Down_Info;

static Eina_Bool               _ecore_x_last_event_mouse_move         = EINA_FALSE;
static Ecore_Event            *_ecore_x_last_event_mouse_move_event   = NULL;
static Ecore_Event_Mouse_Move *_ecore_x_last_event_mouse_move_event_e = NULL;
static Eina_Inlist            *_ecore_x_mouse_down_info_list          = NULL;

#ifdef ECORE_XKB
static Eina_Hash *emitted_events = NULL;
#endif

static void
_ecore_x_mouse_down_info_clear(void)
{
   Eina_Inlist *l = _ecore_x_mouse_down_info_list;
   Ecore_X_Mouse_Down_Info *info = NULL;
   while (l)
     {
        info = EINA_INLIST_CONTAINER_GET(l, Ecore_X_Mouse_Down_Info);
        l = eina_inlist_remove(l, l);
        free(info);
     }
   _ecore_x_mouse_down_info_list = NULL;
}

void
_ecore_x_events_init(void)
{
   //Actually, Nothing to do.
#ifdef ECORE_XKB
   emitted_events = eina_hash_int64_new(NULL);
#endif
}

void
_ecore_x_events_shutdown(void)
{
   _ecore_x_mouse_down_info_clear();
#ifdef ECORE_XKB
   eina_hash_free(emitted_events);
#endif
}

static Ecore_X_Mouse_Down_Info *
_ecore_x_mouse_down_info_get(int dev)
{
   Eina_Inlist *l = _ecore_x_mouse_down_info_list;
   Ecore_X_Mouse_Down_Info *info = NULL;

   //Return the exist info
   EINA_INLIST_FOREACH(l, info)
     if (info->dev == dev) return info;

   //New Device. Add it.
   info = calloc(1, sizeof(Ecore_X_Mouse_Down_Info));
   if (!info) return NULL;

   info->dev = dev;
   l = eina_inlist_append(l, (Eina_Inlist *)info);
   _ecore_x_mouse_down_info_list = l;
   return info;
}

static void
_ecore_x_event_free_mouse_move(void *data EINA_UNUSED,
                               void *ev)
{
   Ecore_Event_Mouse_Move *e;

   e = ev;
   if (_ecore_x_last_event_mouse_move)
     {
        _ecore_x_last_event_mouse_move_event = NULL;
        _ecore_x_last_event_mouse_move_event_e = NULL;
        _ecore_x_last_event_mouse_move = EINA_FALSE;
     }

   free(e);
}

EAPI void
ecore_x_event_mask_set(Ecore_X_Window w,
                       Ecore_X_Event_Mask mask)
{
   XWindowAttributes attr;
   XSetWindowAttributes s_attr;

   LOGFN;

   EINA_SAFETY_ON_NULL_RETURN(_ecore_x_disp);

   if (!w)
     w = DefaultRootWindow(_ecore_x_disp);

   memset(&attr, 0, sizeof(XWindowAttributes));
   XGetWindowAttributes(_ecore_x_disp, w, &attr);
   if (_ecore_xlib_sync) ecore_x_sync();
   s_attr.event_mask = mask | attr.your_event_mask;
   XChangeWindowAttributes(_ecore_x_disp, w, CWEventMask, &s_attr);
   if (_ecore_xlib_sync) ecore_x_sync();
}

EAPI void
ecore_x_event_mask_unset(Ecore_X_Window w,
                         Ecore_X_Event_Mask mask)
{
   XWindowAttributes attr;
   XSetWindowAttributes s_attr;

   LOGFN;

   EINA_SAFETY_ON_NULL_RETURN(_ecore_x_disp);

   if (!w)
     w = DefaultRootWindow(_ecore_x_disp);

   memset(&attr, 0, sizeof(XWindowAttributes));
   XGetWindowAttributes(_ecore_x_disp, w, &attr);
   if (_ecore_xlib_sync) ecore_x_sync();
   s_attr.event_mask = attr.your_event_mask & ~mask;
   XChangeWindowAttributes(_ecore_x_disp, w, CWEventMask, &s_attr);
   if (_ecore_xlib_sync) ecore_x_sync();
}

static void
_ecore_x_event_free_xdnd_enter(void *data EINA_UNUSED,
                               void *ev)
{
   Ecore_X_Event_Xdnd_Enter *e;
   int i;

   e = ev;
   for (i = 0; i < e->num_types; i++)
     XFree(e->types[i]);
   free(e->types);
   free(e);
}

static void
_ecore_x_event_free_selection_notify(void *data EINA_UNUSED,
                                     void *ev)
{
   Ecore_X_Event_Selection_Notify *e;
   Ecore_X_Selection_Data *sel;

   e = ev;
   sel = e->data;
   if (sel->free)
     sel->free(sel);

   free(e->target);
   free(e);
}

static unsigned int
_ecore_x_event_modifiers(unsigned int state)
{
   unsigned int modifiers = 0;

   if (state & ECORE_X_MODIFIER_SHIFT)
     modifiers |= ECORE_EVENT_MODIFIER_SHIFT;

   if (state & ECORE_X_MODIFIER_CTRL)
     modifiers |= ECORE_EVENT_MODIFIER_CTRL;

   if (state & ECORE_X_MODIFIER_ALT)
     modifiers |= ECORE_EVENT_MODIFIER_ALT;

   if (state & ECORE_X_MODIFIER_WIN)
     modifiers |= ECORE_EVENT_MODIFIER_WIN;

   if (state & ECORE_X_MODIFIER_ALTGR)
     modifiers |= ECORE_EVENT_MODIFIER_ALTGR;

   if (state & ECORE_X_LOCK_SCROLL)
     modifiers |= ECORE_EVENT_LOCK_SCROLL;

   if (state & ECORE_X_LOCK_NUM)
     modifiers |= ECORE_EVENT_LOCK_NUM;

   if (state & ECORE_X_LOCK_CAPS)
     modifiers |= ECORE_EVENT_LOCK_CAPS;

   if (state & ECORE_X_LOCK_SHIFT)
     modifiers |= ECORE_EVENT_LOCK_SHIFT;

   return modifiers;
}

void
_ecore_mouse_move(unsigned int timestamp,
                  unsigned int xmodifiers,
                  int x,
                  int y,
                  int x_root,
                  int y_root,
                  unsigned int event_window,
                  unsigned int window,
                  unsigned int root_win,
                  int same_screen,
                  int dev,
                  double radx,
                  double rady,
                  double pressure,
                  double angle,
                  double mx,
                  double my,
                  double mrx,
                  double mry)
{
   Ecore_Event_Mouse_Move *e;
   Ecore_Event *event;

   e = calloc(1, sizeof(Ecore_Event_Mouse_Move));
   if (!e)
     return;

   e->window = window;
   e->root_window = root_win;
   e->timestamp = timestamp;
   e->same_screen = same_screen;
   e->event_window = event_window;

   e->modifiers = _ecore_x_event_modifiers(xmodifiers);
   e->x = x;
   e->y = y;
   e->root.x = x_root;
   e->root.y = y_root;

   e->multi.device = dev;
   e->multi.radius = (radx + rady) / 2;
   e->multi.radius_x = radx;
   e->multi.radius_y = rady;
   e->multi.pressure = pressure;
   e->multi.angle = angle;
   e->multi.x = mx;
   e->multi.y = my;
   e->multi.root.x = mrx;
   e->multi.root.y = mry;

   event = ecore_event_add(ECORE_EVENT_MOUSE_MOVE,
                           e,
                           _ecore_x_event_free_mouse_move,
                           NULL);

   _ecore_x_event_last_time = timestamp;
   _ecore_x_event_last_win = window;
   _ecore_x_event_last_root_x = x_root;
   _ecore_x_event_last_root_y = y_root;

   _ecore_x_last_event_mouse_move_event = event;
   _ecore_x_last_event_mouse_move_event_e = e;
   _ecore_x_last_event_mouse_move = EINA_TRUE;
}

static void
_ecore_x_event_free_axis_update_event(void *data EINA_UNUSED, void *ev)
{
   Ecore_Event_Axis_Update *e = ev;
   if (e) free(e->axis);
   free(e);
}

void
_ecore_x_axis_update(Ecore_Window window,
                     Ecore_Window event_window,
                     Ecore_Window root_window,
                     unsigned int timestamp,
                     int devid,
                     int toolid,
                     int naxis,
                     Ecore_Axis *axis)
{
   Ecore_Event_Axis_Update *e;
   int i;

   e = calloc(1, sizeof(Ecore_Event_Axis_Update));
   if (!e)
     {
        if (axis) free(axis);
        return;
     }

   e->window = window;
   e->event_window = event_window;
   e->root_window = root_window;

   e->timestamp = timestamp;

   e->device = devid;
   e->toolid = toolid;

   e->naxis = naxis;
   e->axis = axis;

   INF("Axis update [%d] (%d) with %d events:", ECORE_EVENT_AXIS_UPDATE, e->device, e->naxis);
   for (i = 0; i < naxis; i++)
     {
        INF("AXIS %d = %f", e->axis[i].label, e->axis[i].value);
     }

   ecore_event_add(ECORE_EVENT_AXIS_UPDATE, e, _ecore_x_event_free_axis_update_event, NULL);

   _ecore_x_event_last_time = timestamp;
}

static void
_ecore_key_press(int event,
                 XKeyEvent *xevent)
{
   Ecore_Event_Key *e;
   char *compose = NULL;
   char *tmp = NULL;
   char *keyname;
   char *key = NULL;
   char keyname_buffer[256];
   char compose_buffer[256];
   KeySym sym, sym2 = 0;
   XComposeStatus status;
   int val;
   int key_len, keyname_len, compose_len;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   sym = _ecore_x_XKeycodeToKeysym(xevent->display, xevent->keycode, 0);
   keyname = XKeysymToString(sym);

   if (!keyname)
     {
        snprintf(keyname_buffer,
                 sizeof(keyname_buffer),
                 "Keycode-%i",
                 xevent->keycode);
        keyname = keyname_buffer;
     }

   key = NULL;
   compose = NULL;
   val = XLookupString(xevent,
                       compose_buffer,
                       sizeof(compose_buffer),
                       &sym2,
                       &status);

   if (sym != sym2)
     key = XKeysymToString(sym2);
   if (!key)
     key = keyname;

   if (val > 0)
     {
        compose_buffer[val] = 0;
        compose = eina_str_convert(nl_langinfo(CODESET), "UTF-8",
                                   compose_buffer);
        if (!compose)
          ERR("Ecore_X cannot convert input key string '%s' to UTF-8. "
              "Is Eina built with iconv support?", compose_buffer);
        tmp = compose;
     }

   key_len = strlen(key);
   keyname_len = strlen(keyname);
   compose_len = (compose) ? strlen(compose) : 0;

   e = calloc(1, sizeof(Ecore_Event_Key) + key_len + keyname_len +
              compose_len + 3);
   if (!e)
     goto on_error;

   e->keyname = (char *)(e + 1);
   e->key = e->keyname + keyname_len + 1;
   e->compose = (compose) ? e->key + key_len + 1 : NULL;
   e->string = e->compose;

   strcpy((char *)e->keyname, keyname);
   strcpy((char *)e->key, key);
   if (compose)
     strcpy((char *)e->compose, compose);

   e->modifiers = _ecore_x_event_modifiers(xevent->state);

   e->timestamp = xevent->time;
   e->window = xevent->subwindow ? xevent->subwindow : xevent->window;
   e->event_window = xevent->window;
   e->same_screen = xevent->same_screen;
   e->root_window = xevent->root;
   e->keycode = xevent->keycode;

   ecore_event_add(event, e, NULL, NULL);

   _ecore_x_event_last_time = e->timestamp;

on_error:
   if (tmp)
     free(tmp);
}

Ecore_Event_Mouse_Button *
_ecore_mouse_button(int event,
                    unsigned int timestamp,
                    unsigned int xmodifiers,
                    unsigned int buttons,
                    int x,
                    int y,
                    int x_root,
                    int y_root,
                    unsigned int event_window,
                    unsigned int window,
                    unsigned int root_win,
                    int same_screen,
                    int dev,
                    double radx,
                    double rady,
                    double pressure,
                    double angle,
                    double mx,
                    double my,
                    double mrx,
                    double mry)
{
   Ecore_Event_Mouse_Button *e;

   e = calloc(1, sizeof(Ecore_Event_Mouse_Button));
   if (!e)
     return NULL;

   e->window = window;
   e->root_window = root_win;
   e->timestamp = timestamp;
   e->same_screen = same_screen;
   e->event_window = event_window;

   e->buttons = buttons;
   e->modifiers = _ecore_x_event_modifiers(xmodifiers);
   e->double_click = 0;
   e->triple_click = 0;
   e->x = x;
   e->y = y;
   e->root.x = x_root;
   e->root.y = y_root;

   Ecore_X_Mouse_Down_Info *down_info = _ecore_x_mouse_down_info_get(dev);

   if (down_info)
     {
        //If mouse cancel event occred, should reset down info related with double & triple click
        if (event == ECORE_EVENT_MOUSE_BUTTON_CANCEL)
          {
             down_info->last_win = 0;
             down_info->last_last_win = 0;
             down_info->last_event_win = 0;
             down_info->last_last_event_win = 0;
             down_info->last_time = 0;
             down_info->last_last_time = 0;
             down_info->did_double = EINA_FALSE;
             down_info->did_triple = EINA_FALSE;
          }
        if ((event == ECORE_EVENT_MOUSE_BUTTON_DOWN) &&
            down_info->did_triple)
          {
             down_info->last_win = 0;
             down_info->last_last_win = 0;
             down_info->last_event_win = 0;
             down_info->last_last_event_win = 0;
             down_info->last_time = 0;
             down_info->last_last_time = 0;
          }
        if (event_window == window)
          {
             if (event == ECORE_EVENT_MOUSE_BUTTON_DOWN)
               {
                  //Check Double Clicked
                  if (((int)(timestamp - down_info->last_time) <=
                       (int)(1000 * _ecore_x_double_click_time)) &&
                      (window == down_info->last_win) &&
                      (event_window == down_info->last_event_win))
                    {
                       e->double_click = 1;
                       down_info->did_double = EINA_TRUE;
                    }
                  else
                    {
                       down_info->did_double = EINA_FALSE;
                       down_info->did_triple = EINA_FALSE;
                    }

                  //Check Triple Clicked
                  if (((int)(timestamp - down_info->last_last_time) <=
                       (int)(2 * 1000 * _ecore_x_double_click_time)) &&
                      (window == down_info->last_win) &&
                      (window == down_info->last_last_win) &&
                      (event_window == down_info->last_event_win) &&
                      (event_window == down_info->last_last_event_win)
                      )
                    {
                       e->triple_click = 1;
                       down_info->did_triple = EINA_TRUE;
                    }
                  else
                    {
                       down_info->did_triple = EINA_FALSE;
                    }
               }
             else if (event == ECORE_EVENT_MOUSE_BUTTON_UP)
               {
                  if (down_info->did_double)
                    e->double_click = 1;
                  if (down_info->did_triple)
                    e->triple_click = 1;
               }
          }
     }

   /* NB: Block commented out as _ecore_x_mouse_up_count appears to have
    * no use. The variable is also commented out above. This code block is
    * the only place that this variable is used, and appears to serve no
    * purpose. - dh
      if (event == ECORE_EVENT_MOUSE_BUTTON_DOWN
       && !e->double_click
       && !e->triple_click)
      _ecore_x_mouse_up_count = 0;
    */

   e->multi.device = dev;
   e->multi.radius = (radx + rady) / 2;
   e->multi.radius_x = radx;
   e->multi.radius_y = rady;
   e->multi.pressure = pressure;
   e->multi.angle = angle;
   e->multi.x = mx;
   e->multi.y = my;
   e->multi.root.x = mrx;
   e->multi.root.y = mry;

   _ecore_x_event_last_time = e->timestamp;
   _ecore_x_event_last_win = e->window;
   _ecore_x_event_last_root_x = x_root;
   _ecore_x_event_last_root_y = y_root;

   ecore_event_add(event, e, NULL, NULL);

   if ((down_info) &&
       (event == ECORE_EVENT_MOUSE_BUTTON_DOWN) &&
       (window == event_window) &&
       (!down_info->did_triple))
     {
        down_info->last_last_win = down_info->last_win;
        down_info->last_win = window;
        down_info->last_last_event_win = down_info->last_event_win;
        down_info->last_event_win = event_window;
        down_info->last_last_time = down_info->last_time;
        down_info->last_time = timestamp;
     }

   return e;
}

void
_ecore_x_event_handle_any_event(XEvent *xevent)
{
   XEvent *ev = malloc(sizeof(XEvent));
   if (!ev) return;
   memcpy(ev, xevent, sizeof(XEvent));
   ecore_event_add(ECORE_X_EVENT_ANY, ev, NULL, NULL);
}

void
_ecore_x_event_handle_key_press(XEvent *xevent)
{
   _ecore_key_press(ECORE_EVENT_KEY_DOWN, (XKeyEvent *)xevent);
}

void
_ecore_x_event_handle_key_release(XEvent *xevent)
{
   _ecore_key_press(ECORE_EVENT_KEY_UP, (XKeyEvent *)xevent);
}

void
_ecore_x_event_handle_button_press(XEvent *xevent)
{
   int i;

   INF("ButtonEvent:press time=%u x=%d y=%d button=%d", (unsigned int)xevent->xbutton.time, (int)xevent->xbutton.x, (int)xevent->xbutton.y, xevent->xbutton.button);

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   if ((xevent->xbutton.button > 3) && (xevent->xbutton.button < 8))
     {
        Ecore_Event_Mouse_Wheel *e;

        e = calloc(1, sizeof(Ecore_Event_Mouse_Wheel));
        if (!e)
          return;

        e->timestamp = xevent->xbutton.time;
        e->modifiers = _ecore_x_event_modifiers(xevent->xbutton.state);
        switch (xevent->xbutton.button)
          {
           case 4: e->direction = 0; e->z = -1; break;

           case 5: e->direction = 0; e->z = 1; break;

           case 6: e->direction = 1; e->z = -1; break;

           case 7: e->direction = 1; e->z = 1; break;

           default: e->direction = 0; e->z = 0; break;
          }

        e->x = xevent->xbutton.x;
        e->y = xevent->xbutton.y;
        e->root.x = xevent->xbutton.x_root;
        e->root.y = xevent->xbutton.y_root;

        if (xevent->xbutton.subwindow)
          e->window = xevent->xbutton.subwindow;
        else
          e->window = xevent->xbutton.window;

        e->event_window = xevent->xbutton.window;
        e->same_screen = xevent->xbutton.same_screen;
        e->root_window = xevent->xbutton.root;

        _ecore_x_event_last_time = e->timestamp;
        _ecore_x_event_last_win = e->window;
        _ecore_x_event_last_root_x = xevent->xbutton.x_root;
        _ecore_x_event_last_root_y = xevent->xbutton.y_root;
        ecore_event_add(ECORE_EVENT_MOUSE_WHEEL, e, NULL, NULL);

        for (i = 0; i < _ecore_window_grabs_num; i++)
          {
             if ((_ecore_window_grabs[i].win == xevent->xbutton.window) ||
                 (_ecore_window_grabs[i].win == xevent->xbutton.subwindow))
               {
                  Eina_Bool replay = EINA_FALSE;

                  if (_ecore_window_grab_replay_func)
                    replay = _ecore_window_grab_replay_func(
                        _ecore_window_grab_replay_data,
                        ECORE_EVENT_MOUSE_WHEEL,
                        e);

                  if (replay)
                    XAllowEvents(xevent->xbutton.display,
                                 ReplayPointer, xevent->xbutton.time);
                  else
                    XAllowEvents(xevent->xbutton.display,
                                 AsyncPointer, xevent->xbutton.time);

                  break;
               }
          }
     }
   else
     {
        {
           _ecore_mouse_move(xevent->xbutton.time, xevent->xbutton.state,
                             xevent->xbutton.x, xevent->xbutton.y,
                             xevent->xbutton.x_root, xevent->xbutton.y_root,
                             xevent->xbutton.window,
                             (xevent->xbutton.subwindow ? xevent->xbutton.
                              subwindow : xevent->xbutton.window),
                             xevent->xbutton.root,
                             xevent->xbutton.same_screen,
                             0, 1, 1,
                             1.0, // pressure
                             0.0, // angle
                             xevent->xbutton.x, xevent->xbutton.y,
                             xevent->xbutton.x_root, xevent->xbutton.y_root);
        }
        {
           Ecore_Event_Mouse_Button *e;
           int event_window;
           int window;

           window =
             (xevent->xbutton.subwindow ? xevent->xbutton.subwindow : xevent->
              xbutton.window);
           event_window = xevent->xbutton.window;

           e = _ecore_mouse_button(ECORE_EVENT_MOUSE_BUTTON_DOWN,
                                   xevent->xbutton.time,
                                   xevent->xbutton.state,
                                   xevent->xbutton.button,
                                   xevent->xbutton.x,
                                   xevent->xbutton.y,
                                   xevent->xbutton.x_root,
                                   xevent->xbutton.y_root,
                                   event_window,
                                   window,
                                   xevent->xbutton.root,
                                   xevent->xbutton.same_screen,
                                   0,
                                   1,
                                   1,
                                   1.0,
// pressure
                                   0.0,
// angle
                                   xevent->xbutton.x,
                                   xevent->xbutton.y,
                                   xevent->xbutton.x_root,
                                   xevent->xbutton.y_root);
           if (e)
             for (i = 0; i < _ecore_window_grabs_num; i++)
               {
                  if ((_ecore_window_grabs[i].win == xevent->xbutton.window) ||
                      (_ecore_window_grabs[i].win == xevent->xbutton.subwindow))
                    {
                       Eina_Bool replay = EINA_FALSE;

                       if (_ecore_window_grab_replay_func)
                         replay = _ecore_window_grab_replay_func(
                             _ecore_window_grab_replay_data,
                             ECORE_EVENT_MOUSE_BUTTON_DOWN,
                             e);

                       if (replay)
                         XAllowEvents(xevent->xbutton.display,
                                      ReplayPointer, xevent->xbutton.time);
                       else
                         XAllowEvents(xevent->xbutton.display,
                                      AsyncPointer, xevent->xbutton.time);

                       break;
                    }
               }
        }
     }
}

void
_ecore_x_event_handle_button_release(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   INF("ButtonEvent:release time=%u x=%d y=%d button=%d", (unsigned int)xevent->xbutton.time, (int)xevent->xbutton.x, (int)xevent->xbutton.y, xevent->xbutton.button);
   /* filter out wheel buttons */
   if ((xevent->xbutton.button <= 3) || (xevent->xbutton.button > 7))
     {
        _ecore_mouse_move(xevent->xbutton.time, xevent->xbutton.state,
                          xevent->xbutton.x, xevent->xbutton.y,
                          xevent->xbutton.x_root, xevent->xbutton.y_root,
                          xevent->xbutton.window,
                          (xevent->xbutton.subwindow ? xevent->xbutton.
                           subwindow : xevent->xbutton.window),
                          xevent->xbutton.root,
                          xevent->xbutton.same_screen,
                          0, 1, 1,
                          1.0, // pressure
                          0.0, // angle
                          xevent->xbutton.x, xevent->xbutton.y,
                          xevent->xbutton.x_root, xevent->xbutton.y_root);

        _ecore_mouse_button(ECORE_EVENT_MOUSE_BUTTON_UP,
                            xevent->xbutton.time, xevent->xbutton.state,
                            xevent->xbutton.button,
                            xevent->xbutton.x, xevent->xbutton.y,
                            xevent->xbutton.x_root, xevent->xbutton.y_root,
                            xevent->xbutton.window,
                            (xevent->xbutton.subwindow ? xevent->xbutton.
                             subwindow : xevent->xbutton.window),
                            xevent->xbutton.root,
                            xevent->xbutton.same_screen,
                            0, 1, 1,
                            1.0, // pressure
                            0.0, // angle
                            xevent->xbutton.x, xevent->xbutton.y,
                            xevent->xbutton.x_root, xevent->xbutton.y_root);
     }
}

void
_ecore_x_event_handle_motion_notify(XEvent *xevent)
{
   if (_ecore_x_last_event_mouse_move)
     {
        Ecore_Event_Mouse_Move *e = _ecore_x_last_event_mouse_move_event_e;

        if ((e) &&
            (xevent->xmotion.window == e->event_window) &&
            (xevent->xmotion.root == e->root_window) &&
            (xevent->xmotion.same_screen == e->same_screen) &&
            (_ecore_x_event_modifiers(xevent->xmotion.state) == e->modifiers))
          {
             if (((xevent->xmotion.subwindow) &&
                  (xevent->xmotion.subwindow == e->window)) ||
                 ((!xevent->xmotion.subwindow) &&
                  (xevent->xmotion.window == e->window)))
               {
                  // XXX: shouldn't we store event history in the new event
                  // with prior x,y,timestamp (all else assumed the same)
                  ecore_event_del(_ecore_x_last_event_mouse_move_event);
                  _ecore_x_last_event_mouse_move = EINA_FALSE;
                  _ecore_x_last_event_mouse_move_event = NULL;
               }
          }
     }

   _ecore_mouse_move(xevent->xmotion.time, xevent->xmotion.state,
                     xevent->xmotion.x, xevent->xmotion.y,
                     xevent->xmotion.x_root, xevent->xmotion.y_root,
                     xevent->xmotion.window,
                     (xevent->xmotion.subwindow ? xevent->xmotion.subwindow :
                      xevent->xmotion.window),
                     xevent->xmotion.root,
                     xevent->xmotion.same_screen,
                     0, 1, 1,
                     1.0,   // pressure
                     0.0,   // angle
                     xevent->xmotion.x, xevent->xmotion.y,
                     xevent->xmotion.x_root, xevent->xmotion.y_root);

   _ecore_x_last_event_mouse_move = EINA_TRUE;

   /* Xdnd handling */
   _ecore_x_dnd_drag(xevent->xmotion.root,
                     xevent->xmotion.x_root,
                     xevent->xmotion.y_root);
}

void
_ecore_x_event_handle_enter_notify(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   {
      _ecore_mouse_move(xevent->xcrossing.time, xevent->xcrossing.state,
                        xevent->xcrossing.x, xevent->xcrossing.y,
                        xevent->xcrossing.x_root, xevent->xcrossing.y_root,
                        xevent->xcrossing.window,
                        (xevent->xcrossing.subwindow ? xevent->xcrossing.
                         subwindow : xevent->xcrossing.window),
                        xevent->xcrossing.root,
                        xevent->xcrossing.same_screen,
                        0, 1, 1,
                        1.0, // pressure
                        0.0, // angle
                        xevent->xcrossing.x, xevent->xcrossing.y,
                        xevent->xcrossing.x_root, xevent->xcrossing.y_root);
   }
   {
      Ecore_X_Event_Mouse_In *e;

      e = calloc(1, sizeof(Ecore_X_Event_Mouse_In));
      if (!e)
        return;

      e->modifiers = _ecore_x_event_modifiers(xevent->xcrossing.state);
      e->x = xevent->xcrossing.x;
      e->y = xevent->xcrossing.y;
      e->root.x = xevent->xcrossing.x_root;
      e->root.y = xevent->xcrossing.y_root;
      if (xevent->xcrossing.subwindow)
        e->win = xevent->xcrossing.subwindow;
      else
        e->win = xevent->xcrossing.window;

      e->same_screen = xevent->xcrossing.same_screen;
      e->root_win = xevent->xcrossing.root;
      e->event_win = xevent->xcrossing.window;

      if (xevent->xcrossing.mode == NotifyNormal)
        e->mode = ECORE_X_EVENT_MODE_NORMAL;
      else if (xevent->xcrossing.mode == NotifyGrab)
        e->mode = ECORE_X_EVENT_MODE_GRAB;
      else if (xevent->xcrossing.mode == NotifyUngrab)
        e->mode = ECORE_X_EVENT_MODE_UNGRAB;

      if (xevent->xcrossing.detail == NotifyAncestor)
        e->detail = ECORE_X_EVENT_DETAIL_ANCESTOR;
      else if (xevent->xcrossing.detail == NotifyVirtual)
        e->detail = ECORE_X_EVENT_DETAIL_VIRTUAL;
      else if (xevent->xcrossing.detail == NotifyInferior)
        e->detail = ECORE_X_EVENT_DETAIL_INFERIOR;
      else if (xevent->xcrossing.detail == NotifyNonlinear)
        e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR;
      else if (xevent->xcrossing.detail == NotifyNonlinearVirtual)
        e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR_VIRTUAL;

      e->time = xevent->xcrossing.time;
      _ecore_x_event_last_time = e->time;
      ecore_event_add(ECORE_X_EVENT_MOUSE_IN, e, NULL, NULL);
   }
}

void
_ecore_x_event_handle_leave_notify(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   {
      _ecore_mouse_move(xevent->xcrossing.time, xevent->xcrossing.state,
                        xevent->xcrossing.x, xevent->xcrossing.y,
                        xevent->xcrossing.x_root, xevent->xcrossing.y_root,
                        xevent->xcrossing.window,
                        (xevent->xcrossing.subwindow ? xevent->xcrossing.
                         subwindow : xevent->xcrossing.window),
                        xevent->xcrossing.root,
                        xevent->xcrossing.same_screen,
                        0, 1, 1,
                        1.0, // pressure
                        0.0, // angle
                        xevent->xcrossing.x, xevent->xcrossing.y,
                        xevent->xcrossing.x_root, xevent->xcrossing.y_root);
   }
   {
      Ecore_X_Event_Mouse_Out *e;

      e = calloc(1, sizeof(Ecore_X_Event_Mouse_Out));
      if (!e)
        return;

      e->modifiers = _ecore_x_event_modifiers(xevent->xcrossing.state);
      e->x = xevent->xcrossing.x;
      e->y = xevent->xcrossing.y;
      e->root.x = xevent->xcrossing.x_root;
      e->root.y = xevent->xcrossing.y_root;
      if (xevent->xcrossing.subwindow)
        e->win = xevent->xcrossing.subwindow;
      else
        e->win = xevent->xcrossing.window;

      e->same_screen = xevent->xcrossing.same_screen;
      e->root_win = xevent->xcrossing.root;
      e->event_win = xevent->xcrossing.window;

      if (xevent->xcrossing.mode == NotifyNormal)
        e->mode = ECORE_X_EVENT_MODE_NORMAL;
      else if (xevent->xcrossing.mode == NotifyGrab)
        e->mode = ECORE_X_EVENT_MODE_GRAB;
      else if (xevent->xcrossing.mode == NotifyUngrab)
        e->mode = ECORE_X_EVENT_MODE_UNGRAB;

      if (xevent->xcrossing.detail == NotifyAncestor)
        e->detail = ECORE_X_EVENT_DETAIL_ANCESTOR;
      else if (xevent->xcrossing.detail == NotifyVirtual)
        e->detail = ECORE_X_EVENT_DETAIL_VIRTUAL;
      else if (xevent->xcrossing.detail == NotifyInferior)
        e->detail = ECORE_X_EVENT_DETAIL_INFERIOR;
      else if (xevent->xcrossing.detail == NotifyNonlinear)
        e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR;
      else if (xevent->xcrossing.detail == NotifyNonlinearVirtual)
        e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR_VIRTUAL;

      e->time = xevent->xcrossing.time;
      _ecore_x_event_last_time = e->time;
      _ecore_x_event_last_win = e->win;
      _ecore_x_event_last_root_x = e->root.x;
      _ecore_x_event_last_root_y = e->root.y;
      ecore_event_add(ECORE_X_EVENT_MOUSE_OUT, e, NULL, NULL);
   }
}

void
_ecore_x_event_handle_focus_in(XEvent *xevent)
{
   Ecore_X_Event_Window_Focus_In *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;

   e = calloc(1, sizeof(Ecore_X_Event_Window_Focus_In));
   if (!e)
     return;

   e->win = xevent->xfocus.window;

   if (xevent->xfocus.mode == NotifyNormal)
     e->mode = ECORE_X_EVENT_MODE_NORMAL;
   else if (xevent->xfocus.mode == NotifyWhileGrabbed)
     e->mode = ECORE_X_EVENT_MODE_WHILE_GRABBED;
   else if (xevent->xfocus.mode == NotifyGrab)
     e->mode = ECORE_X_EVENT_MODE_GRAB;
   else if (xevent->xfocus.mode == NotifyUngrab)
     e->mode = ECORE_X_EVENT_MODE_UNGRAB;

   if (xevent->xfocus.detail == NotifyAncestor)
     e->detail = ECORE_X_EVENT_DETAIL_ANCESTOR;
   else if (xevent->xfocus.detail == NotifyVirtual)
     e->detail = ECORE_X_EVENT_DETAIL_VIRTUAL;
   else if (xevent->xfocus.detail == NotifyInferior)
     e->detail = ECORE_X_EVENT_DETAIL_INFERIOR;
   else if (xevent->xfocus.detail == NotifyNonlinear)
     e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR;
   else if (xevent->xfocus.detail == NotifyNonlinearVirtual)
     e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR_VIRTUAL;
   else if (xevent->xfocus.detail == NotifyPointer)
     e->detail = ECORE_X_EVENT_DETAIL_POINTER;
   else if (xevent->xfocus.detail == NotifyPointerRoot)
     e->detail = ECORE_X_EVENT_DETAIL_POINTER_ROOT;
   else if (xevent->xfocus.detail == NotifyDetailNone)
     e->detail = ECORE_X_EVENT_DETAIL_DETAIL_NONE;

   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_FOCUS_IN, e, NULL, NULL);
}

void
_ecore_x_event_handle_focus_out(XEvent *xevent)
{
   Ecore_X_Event_Window_Focus_Out *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;

   e = calloc(1, sizeof(Ecore_X_Event_Window_Focus_Out));
   if (!e)
     return;

   e->win = xevent->xfocus.window;

   if (xevent->xfocus.mode == NotifyNormal)
     e->mode = ECORE_X_EVENT_MODE_NORMAL;
   else if (xevent->xfocus.mode == NotifyWhileGrabbed)
     e->mode = ECORE_X_EVENT_MODE_WHILE_GRABBED;
   else if (xevent->xfocus.mode == NotifyGrab)
     e->mode = ECORE_X_EVENT_MODE_GRAB;
   else if (xevent->xfocus.mode == NotifyUngrab)
     e->mode = ECORE_X_EVENT_MODE_UNGRAB;

   if (xevent->xfocus.detail == NotifyAncestor)
     e->detail = ECORE_X_EVENT_DETAIL_ANCESTOR;
   else if (xevent->xfocus.detail == NotifyVirtual)
     e->detail = ECORE_X_EVENT_DETAIL_VIRTUAL;
   else if (xevent->xfocus.detail == NotifyInferior)
     e->detail = ECORE_X_EVENT_DETAIL_INFERIOR;
   else if (xevent->xfocus.detail == NotifyNonlinear)
     e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR;
   else if (xevent->xfocus.detail == NotifyNonlinearVirtual)
     e->detail = ECORE_X_EVENT_DETAIL_NON_LINEAR_VIRTUAL;
   else if (xevent->xfocus.detail == NotifyPointer)
     e->detail = ECORE_X_EVENT_DETAIL_POINTER;
   else if (xevent->xfocus.detail == NotifyPointerRoot)
     e->detail = ECORE_X_EVENT_DETAIL_POINTER_ROOT;
   else if (xevent->xfocus.detail == NotifyDetailNone)
     e->detail = ECORE_X_EVENT_DETAIL_DETAIL_NONE;

   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_FOCUS_OUT, e, NULL, NULL);
}

void
_ecore_x_event_handle_keymap_notify(XEvent *xevent EINA_UNUSED)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   /* FIXME: handle this event type */
}

void
_ecore_x_event_handle_expose(XEvent *xevent)
{
   Ecore_X_Event_Window_Damage *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Damage));
   if (!e)
     return;

   e->win = xevent->xexpose.window;
   e->time = _ecore_x_event_last_time;
   e->x = xevent->xexpose.x;
   e->y = xevent->xexpose.y;
   e->w = xevent->xexpose.width;
   e->h = xevent->xexpose.height;
   e->count = xevent->xexpose.count;
   ecore_event_add(ECORE_X_EVENT_WINDOW_DAMAGE, e, NULL, NULL);
}

void
_ecore_x_event_handle_graphics_expose(XEvent *xevent)
{
   Ecore_X_Event_Window_Damage *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Damage));
   if (!e)
     return;

   e->win = xevent->xgraphicsexpose.drawable;
   e->time = _ecore_x_event_last_time;
   e->x = xevent->xgraphicsexpose.x;
   e->y = xevent->xgraphicsexpose.y;
   e->w = xevent->xgraphicsexpose.width;
   e->h = xevent->xgraphicsexpose.height;
   e->count = xevent->xgraphicsexpose.count;
   ecore_event_add(ECORE_X_EVENT_WINDOW_DAMAGE, e, NULL, NULL);
}

void
_ecore_x_event_handle_visibility_notify(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
//   if (xevent->xvisibility.state != VisibilityPartiallyObscured)
   {
      Ecore_X_Event_Window_Visibility_Change *e;

      e = calloc(1, sizeof(Ecore_X_Event_Window_Visibility_Change));
      if (!e)
        return;

      e->win = xevent->xvisibility.window;
      e->time = _ecore_x_event_last_time;
      if (xevent->xvisibility.state == VisibilityFullyObscured)
        e->fully_obscured = 1;
      else
        e->fully_obscured = 0;

      ecore_event_add(ECORE_X_EVENT_WINDOW_VISIBILITY_CHANGE, e, NULL, NULL);
   }
}

void
_ecore_x_event_handle_create_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Create *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Create));
   if (!e)
     return;

   e->win = xevent->xcreatewindow.window;
   e->parent = xevent->xcreatewindow.parent;
   if (xevent->xcreatewindow.override_redirect)
     e->override = 1;
   else
     e->override = 0;

   e->x = xevent->xcreatewindow.x;
   e->y = xevent->xcreatewindow.y;
   e->w = xevent->xcreatewindow.width;
   e->h = xevent->xcreatewindow.height;
   e->border = xevent->xcreatewindow.border_width;
   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_CREATE, e, NULL, NULL);
}

void
_ecore_x_event_handle_destroy_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Destroy *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Destroy));
   if (!e)
     return;

   e->win = xevent->xdestroywindow.window;
   e->event_win = xevent->xdestroywindow.event;
   e->time = _ecore_x_event_last_time;
   if (e->win == _ecore_x_event_last_win)
     _ecore_x_event_last_win = 0;

   ecore_event_add(ECORE_X_EVENT_WINDOW_DESTROY, e, NULL, NULL);
   while (_ecore_x_window_grab_remove(e->win, -1, 0, 0));
   while (_ecore_x_key_grab_remove(e->win, NULL, 0, 0));
}

void
_ecore_x_event_handle_unmap_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Hide *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Hide));
   if (!e)
     return;

   e->win = xevent->xunmap.window;
   e->event_win = xevent->xunmap.event;
   e->time = _ecore_x_event_last_time;
   e->send_event = xevent->xunmap.send_event;
   ecore_event_add(ECORE_X_EVENT_WINDOW_HIDE, e, NULL, NULL);
}

void
_ecore_x_event_handle_map_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Show *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Show));
   if (!e)
     return;

   e->win = xevent->xmap.window;
   e->event_win = xevent->xmap.event;
   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_SHOW, e, NULL, NULL);
}

void
_ecore_x_event_handle_map_request(XEvent *xevent)
{
   Ecore_X_Event_Window_Show_Request *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Show_Request));
   if (!e)
     return;

   e->win = xevent->xmaprequest.window;
   e->time = _ecore_x_event_last_time;
   e->parent = xevent->xmaprequest.parent;
   ecore_event_add(ECORE_X_EVENT_WINDOW_SHOW_REQUEST, e, NULL, NULL);
}

void
_ecore_x_event_handle_reparent_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Reparent *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Reparent));
   if (!e)
     return;

   e->win = xevent->xreparent.window;
   e->event_win = xevent->xreparent.event;
   e->parent = xevent->xreparent.parent;
   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_REPARENT, e, NULL, NULL);
}

void
_ecore_x_event_handle_configure_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Configure *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Configure));
   if (!e)
     return;

   e->win = xevent->xconfigure.window;
   e->event_win = xevent->xconfigure.event;
   e->abovewin = xevent->xconfigure.above;
   e->x = xevent->xconfigure.x;
   e->y = xevent->xconfigure.y;
   e->w = xevent->xconfigure.width;
   e->h = xevent->xconfigure.height;
   e->border = xevent->xconfigure.border_width;
   e->override = xevent->xconfigure.override_redirect;
   e->from_wm = xevent->xconfigure.send_event;
   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_CONFIGURE, e, NULL, NULL);
}

void
_ecore_x_event_handle_configure_request(XEvent *xevent)
{
   Ecore_X_Event_Window_Configure_Request *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Configure_Request));
   if (!e)
     return;

   e->win = xevent->xconfigurerequest.window;
   e->parent_win = xevent->xconfigurerequest.parent;
   e->abovewin = xevent->xconfigurerequest.above;
   e->x = xevent->xconfigurerequest.x;
   e->y = xevent->xconfigurerequest.y;
   e->w = xevent->xconfigurerequest.width;
   e->h = xevent->xconfigurerequest.height;
   e->border = xevent->xconfigurerequest.border_width;
   e->value_mask = xevent->xconfigurerequest.value_mask;
   e->time = _ecore_x_event_last_time;

   if (xevent->xconfigurerequest.detail == Above)
     e->detail = ECORE_X_WINDOW_STACK_ABOVE;
   else if (xevent->xconfigurerequest.detail == Below)
     e->detail = ECORE_X_WINDOW_STACK_BELOW;
   else if (xevent->xconfigurerequest.detail == TopIf)
     e->detail = ECORE_X_WINDOW_STACK_TOP_IF;
   else if (xevent->xconfigurerequest.detail == BottomIf)
     e->detail = ECORE_X_WINDOW_STACK_BOTTOM_IF;
   else if (xevent->xconfigurerequest.detail == Opposite)
     e->detail = ECORE_X_WINDOW_STACK_OPPOSITE;

   ecore_event_add(ECORE_X_EVENT_WINDOW_CONFIGURE_REQUEST, e, NULL, NULL);
}

void
_ecore_x_event_handle_gravity_notify(XEvent *xevent EINA_UNUSED)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   /* FIXME: handle this event type */
}

void
_ecore_x_event_handle_resize_request(XEvent *xevent)
{
   Ecore_X_Event_Window_Resize_Request *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Resize_Request));
   if (!e)
     return;

   e->win = xevent->xresizerequest.window;
   e->w = xevent->xresizerequest.width;
   e->h = xevent->xresizerequest.height;
   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_RESIZE_REQUEST, e, NULL, NULL);
}

void
_ecore_x_event_handle_circulate_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Stack *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Stack));
   if (!e)
     return;

   e->win = xevent->xcirculate.window;
   e->event_win = xevent->xcirculate.event;
   if (xevent->xcirculate.place == PlaceOnTop)
     e->detail = ECORE_X_WINDOW_STACK_ABOVE;
   else
     e->detail = ECORE_X_WINDOW_STACK_BELOW;

   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_STACK, e, NULL, NULL);
}

void
_ecore_x_event_handle_circulate_request(XEvent *xevent)
{
   Ecore_X_Event_Window_Stack_Request *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Stack_Request));
   if (!e)
     return;

   e->win = xevent->xcirculaterequest.window;
   e->parent = xevent->xcirculaterequest.parent;
   if (xevent->xcirculaterequest.place == PlaceOnTop)
     e->detail = ECORE_X_WINDOW_STACK_ABOVE;
   else
     e->detail = ECORE_X_WINDOW_STACK_BELOW;

   e->time = _ecore_x_event_last_time;
   ecore_event_add(ECORE_X_EVENT_WINDOW_STACK_REQUEST, e, NULL, NULL);
}

void
_ecore_x_event_handle_property_notify(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   {
      Ecore_X_Event_Window_Property *e;

      e = calloc(1, sizeof(Ecore_X_Event_Window_Property));
      if (!e)
        return;

      e->win = xevent->xproperty.window;
      e->atom = xevent->xproperty.atom;
      e->time = xevent->xproperty.time;
      e->state = !!xevent->xproperty.state;
      _ecore_x_event_last_time = e->time;
      ecore_event_add(ECORE_X_EVENT_WINDOW_PROPERTY, e, NULL, NULL);
   }
}

void
_ecore_x_event_handle_selection_clear(XEvent *xevent)
{
   Ecore_X_Selection_Intern *d;
   Ecore_X_Event_Selection_Clear *e;
   Ecore_X_Atom sel;

   LOGFN;
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   d = _ecore_x_selection_get(xevent->xselectionclear.selection);
   if (d && (xevent->xselectionclear.time <= d->time)) return;
/* errr..... why? paranoia.
   if (d && (xevent->xselectionclear.time > d->time))
     {
        _ecore_x_selection_set(None, NULL, 0,
                               xevent->xselectionclear.selection);
     }
 */
/* Generate event for app cleanup */
   e = malloc(sizeof(Ecore_X_Event_Selection_Clear));
   e->win = xevent->xselectionclear.window;
   e->time = xevent->xselectionclear.time;
   e->atom = sel = xevent->xselectionclear.selection;
   if (sel == ECORE_X_ATOM_SELECTION_PRIMARY)
     e->selection = ECORE_X_SELECTION_PRIMARY;
   else if (sel == ECORE_X_ATOM_SELECTION_SECONDARY)
     e->selection = ECORE_X_SELECTION_SECONDARY;
   else if (sel == ECORE_X_ATOM_SELECTION_XDND)
     e->selection = ECORE_X_SELECTION_XDND;
   else if (sel == ECORE_X_ATOM_SELECTION_CLIPBOARD)
     e->selection = ECORE_X_SELECTION_CLIPBOARD;
   else
     e->selection = ECORE_X_SELECTION_OTHER;

   ecore_event_add(ECORE_X_EVENT_SELECTION_CLEAR, e, NULL, NULL);
}

void
_ecore_x_event_handle_selection_request(XEvent *xevent)
{
   Ecore_X_Event_Selection_Request *e;
   Ecore_X_Selection_Intern *sd;
   void *data = NULL;
   int len;
   int typesize;

   LOGFN;
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   /*
    * Generate a selection request event.
    */
   e = malloc(sizeof(Ecore_X_Event_Selection_Request));
   e->owner = xevent->xselectionrequest.owner;
   e->requestor = xevent->xselectionrequest.requestor;
   e->time = xevent->xselectionrequest.time;
   e->selection = xevent->xselectionrequest.selection;
   e->target = xevent->xselectionrequest.target;
   e->property = xevent->xselectionrequest.property;
   ecore_event_add(ECORE_X_EVENT_SELECTION_REQUEST, e, NULL, NULL);

   if ((sd = _ecore_x_selection_get(xevent->xselectionrequest.selection)) &&
       (sd->win == xevent->xselectionrequest.owner))
     {
        Ecore_X_Selection_Intern *si;

        si = _ecore_x_selection_get(xevent->xselectionrequest.selection);
        if (si->data)
          {
             Ecore_X_Atom property = None;
             Ecore_X_Atom type;

             /* Set up defaults for strings first */
             type = xevent->xselectionrequest.target;
             typesize = 8;
             len = sd->length;

             if (!ecore_x_selection_convert(xevent->xselectionrequest.selection,
                                            xevent->xselectionrequest.target,
                                            &data, &len, &type, &typesize))
               /* Refuse selection, conversion to requested target failed */
               property = None;
             else if (data)
               {
                  /* FIXME: This does not properly handle large data transfers */
                  ecore_x_window_prop_property_set(
                    xevent->xselectionrequest.requestor,
                    xevent->xselectionrequest.
                    property,
                    type,
                    typesize,
                    data,
                    len);
                  property = xevent->xselectionrequest.property;
                  free(data);
               }

             ecore_x_selection_notify_send(xevent->xselectionrequest.requestor,
                                           xevent->xselectionrequest.selection,
                                           xevent->xselectionrequest.target,
                                           property,
                                           xevent->xselectionrequest.time);
          }
     }
}

void
_ecore_x_event_handle_selection_notify(XEvent *xevent)
{
   Ecore_X_Event_Selection_Notify *e;
   unsigned char *data = NULL;
   Ecore_X_Atom selection;
   int num_ret, format;

   LOGFN;
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   selection = xevent->xselection.selection;

   if (xevent->xselection.target == ECORE_X_ATOM_SELECTION_TARGETS)
     {
        format = ecore_x_window_prop_property_get(xevent->xselection.requestor,
                                                  xevent->xselection.property,
                                                  XA_ATOM, 32, &data, &num_ret);
        if (!format)
          {
             /* fallback if targets handling is not working and try get the
              * selection directly */
             XConvertSelection(_ecore_x_disp, selection,
                               ECORE_X_ATOM_UTF8_STRING,
                               selection,
                               xevent->xselection.requestor,
                               CurrentTime);
             if (data) free(data);
             return;
          }
     }
   else
     {
        format = ecore_x_window_prop_property_get(xevent->xselection.requestor,
                                                  xevent->xselection.property,
                                                  AnyPropertyType, 8, &data,
                                                  &num_ret);
        if (!format) return;
     }

   e = calloc(1, sizeof(Ecore_X_Event_Selection_Notify));
   if (!e)
     {
        if (data) free(data);
        return;
     }

   e->win = xevent->xselection.requestor;
   e->time = xevent->xselection.time;
   e->atom = selection;
   e->property = xevent->xselection.property;
   e->target = _ecore_x_selection_target_get(xevent->xselection.target);

   if (selection == ECORE_X_ATOM_SELECTION_PRIMARY)
     e->selection = ECORE_X_SELECTION_PRIMARY;
   else if (selection == ECORE_X_ATOM_SELECTION_SECONDARY)
     e->selection = ECORE_X_SELECTION_SECONDARY;
   else if (selection == ECORE_X_ATOM_SELECTION_XDND)
     e->selection = ECORE_X_SELECTION_XDND;
   else if (selection == ECORE_X_ATOM_SELECTION_CLIPBOARD)
     e->selection = ECORE_X_SELECTION_CLIPBOARD;
   else
     e->selection = ECORE_X_SELECTION_OTHER;

   e->data = _ecore_x_selection_parse(e->target, data, num_ret, format);

   ecore_event_add(ECORE_X_EVENT_SELECTION_NOTIFY, e,
                   _ecore_x_event_free_selection_notify, NULL);
}

void
_ecore_x_event_handle_colormap_notify(XEvent *xevent)
{
   Ecore_X_Event_Window_Colormap *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Colormap));
   if (!e)
     return;

   e->win = xevent->xcolormap.window;
   e->cmap = xevent->xcolormap.colormap;
   e->time = _ecore_x_event_last_time;
   if (xevent->xcolormap.state == ColormapInstalled)
     e->installed = EINA_TRUE;
   else
     e->installed = EINA_FALSE;

   ecore_event_add(ECORE_X_EVENT_WINDOW_COLORMAP, e, NULL, NULL);
}

void
_ecore_x_event_handle_client_message(XEvent *xevent)
{
   _ecore_x_last_event_mouse_move = EINA_FALSE;
   /* Special client message event handling here. need to put LOTS of if */
   /* checks here and generate synthetic events per special message known */
   /* otherwise generate generic client message event. this would handle*/
   /* netwm, ICCCM, gnomewm, old kde and mwm hint client message protocols */
   if ((xevent->xclient.message_type == ECORE_X_ATOM_WM_PROTOCOLS) &&
       (xevent->xclient.format == 32) &&
       (xevent->xclient.data.l[0] == (long)ECORE_X_ATOM_WM_DELETE_WINDOW))
     {
        Ecore_X_Event_Window_Delete_Request *e;

        e = calloc(1, sizeof(Ecore_X_Event_Window_Delete_Request));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->time = _ecore_x_event_last_time;
        ecore_event_add(ECORE_X_EVENT_WINDOW_DELETE_REQUEST, e, NULL, NULL);
     }
   else if ((xevent->xclient.message_type == ECORE_X_ATOM_NET_WM_MOVERESIZE) &&
            (xevent->xclient.format == 32) &&
/* Ignore move and resize with keyboard */
            (xevent->xclient.data.l[2] < 9))
     {
        Ecore_X_Event_Window_Move_Resize_Request *e;

        e = calloc(1, sizeof(Ecore_X_Event_Window_Move_Resize_Request));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->x = xevent->xclient.data.l[0];
        e->y = xevent->xclient.data.l[1];
        e->direction = xevent->xclient.data.l[2];
        e->button = xevent->xclient.data.l[3];
        e->source = xevent->xclient.data.l[4];
        ecore_event_add(ECORE_X_EVENT_WINDOW_MOVE_RESIZE_REQUEST, e, NULL, NULL);
     }
   /* Xdnd Client Message Handling Begin */
   /* Message Type: XdndEnter target */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_ENTER)
     {
        Ecore_X_Event_Xdnd_Enter *e;
        Ecore_X_DND_Target *target;

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Enter));
        if (!e) return;

        LOGFN;

        target = _ecore_x_dnd_target_get();
        target->state = ECORE_X_DND_TARGET_ENTERED;
        target->source = xevent->xclient.data.l[0];
        target->win = xevent->xclient.window;
        target->version = (int)(xevent->xclient.data.l[1] >> 24);
        if (target->version > ECORE_X_DND_VERSION)
          {
             WRN("DND: Requested version %d, we only support up to %d",
                 target->version, ECORE_X_DND_VERSION);
             free(e);
             return;
          }

        if (xevent->xclient.data.l[1] & 0x1UL)
          {
             /* source supports more than 3 types, fetch property */
             unsigned char *data;
             Ecore_X_Atom *types;
             int i, num_ret;

             LOGFN;
             if (!(ecore_x_window_prop_property_get(target->source,
                                                    ECORE_X_ATOM_XDND_TYPE_LIST,
                                                    XA_ATOM,
                                                    32, &data, &num_ret)))
               {
                  WRN(
                    "DND: Could not fetch data type list from source window, aborting.");
                  if (data) free(data);
                  free(e);
                  return;
               }

             types = (Ecore_X_Atom *)data;
             e->types = calloc(num_ret, sizeof(char *));
             if (e->types)
               {
                  LOGFN;
                  for (i = 0; i < num_ret; i++)
                    e->types[i] = XGetAtomName(_ecore_x_disp, types[i]);
               }

             e->num_types = num_ret;
             if (data) free(data);
          }
        else
          {
             int i = 0;

             e->types = calloc(3, sizeof(char *));
             if (e->types)
               {
                  LOGFN;
                  while ((i < 3) && (xevent->xclient.data.l[i + 2]))
                    {
                       e->types[i] = XGetAtomName(_ecore_x_disp,
                                                  xevent->xclient.data.l[i + 2]);
                       i++;
                    }
               }

             e->num_types = i;
          }

        e->win = target->win;
        e->source = target->source;
        ecore_event_add(ECORE_X_EVENT_XDND_ENTER, e,
                        _ecore_x_event_free_xdnd_enter, NULL);
     }
   /* Message Type: XdndPosition target */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_POSITION)
     {
        Ecore_X_Event_Xdnd_Position *e;
        Ecore_X_DND_Target *target;

        LOGFN;

        target = _ecore_x_dnd_target_get();
        if ((target->source != (Ecore_X_Window)xevent->xclient.data.l[0]) ||
            (target->win != xevent->xclient.window))
          return;

        target->pos.x = xevent->xclient.data.l[2] >> 16;
        target->pos.y = xevent->xclient.data.l[2] & 0xFFFFUL;
        target->action = xevent->xclient.data.l[4]; /* Version 2 */

        target->time = (target->version >= 1) ?
          (Time)xevent->xclient.data.l[3] : CurrentTime;

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Position));
        if (!e) return;

        e->win = target->win;
        e->source = target->source;
        e->position.x = target->pos.x;
        e->position.y = target->pos.y;
        e->action = target->action;
        ecore_event_add(ECORE_X_EVENT_XDND_POSITION, e, NULL, NULL);
     }
   /* Message Type: XdndStatus source */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_STATUS)
     {
        Ecore_X_Event_Xdnd_Status *e;
        Ecore_X_DND_Source *source;

        LOGFN;

        source = _ecore_x_dnd_source_get();
        /* Make sure source/target match */
        if ((source->win != xevent->xclient.window) ||
            (source->dest != (Window)xevent->xclient.data.l[0]))
          return;

        source->await_status = 0;

        source->will_accept = xevent->xclient.data.l[1] & 0x1UL;
        source->suppress = (xevent->xclient.data.l[1] & 0x2UL) ? 0 : 1;

        source->rectangle.x = xevent->xclient.data.l[2] >> 16;
        source->rectangle.y = xevent->xclient.data.l[2] & 0xFFFFUL;
        source->rectangle.width = xevent->xclient.data.l[3] >> 16;
        source->rectangle.height = xevent->xclient.data.l[3] & 0xFFFFUL;

        source->accepted_action = xevent->xclient.data.l[4];

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Status));
        if (!e) return;

        e->win = source->win;
        e->target = source->dest;
        e->will_accept = source->will_accept;
        e->rectangle.x = source->rectangle.x;
        e->rectangle.y = source->rectangle.y;
        e->rectangle.width = source->rectangle.width;
        e->rectangle.height = source->rectangle.height;
        e->action = source->accepted_action;

        ecore_event_add(ECORE_X_EVENT_XDND_STATUS, e, NULL, NULL);
     }
   /* Message Type: XdndLeave target */
   /* Pretend the whole thing never happened, sort of */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_LEAVE)
     {
        Ecore_X_Event_Xdnd_Leave *e;
        Ecore_X_DND_Target *target;

        LOGFN;

        target = _ecore_x_dnd_target_get();
        if ((target->source != (Ecore_X_Window)xevent->xclient.data.l[0]) ||
            (target->win != xevent->xclient.window))
          return;

        target->state = ECORE_X_DND_TARGET_IDLE;

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Leave));
        if (!e) return;

        e->win = xevent->xclient.window;
        e->source = (Window)xevent->xclient.data.l[0];
        ecore_event_add(ECORE_X_EVENT_XDND_LEAVE, e, NULL, NULL);
     }
   /* Message Type: XdndDrop target */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_DROP)
     {
        Ecore_X_Event_Xdnd_Drop *e;
        Ecore_X_DND_Target *target;

        LOGFN;

        target = _ecore_x_dnd_target_get();
        /* Match source/target */
        if ((target->source != (Window)xevent->xclient.data.l[0]) ||
            (target->win != xevent->xclient.window))
          return;

        target->time = (target->version >= 1) ?
          (Time)xevent->xclient.data.l[2] : _ecore_x_event_last_time;

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Drop));
        if (!e) return;

        e->win = target->win;
        e->source = target->source;
        e->action = target->action;
        e->position.x = target->pos.x;
        e->position.y = target->pos.y;
        ecore_event_add(ECORE_X_EVENT_XDND_DROP, e, NULL, NULL);
     }
   /* Message Type: XdndFinished source */
   else if (xevent->xclient.message_type == ECORE_X_ATOM_XDND_FINISHED)
     {
        Ecore_X_Event_Xdnd_Finished *e;
        Ecore_X_DND_Source *source;
        Eina_Bool completed = EINA_TRUE;

        LOGFN;

        source = _ecore_x_dnd_source_get();
        /* Match source/target */
        if ((source->win != xevent->xclient.window) ||
            (source->dest != (Window)xevent->xclient.data.l[0]))
          return;

        if ((source->version < 5) || (xevent->xclient.data.l[1] & 0x1UL))
          {
             LOGFN;
             /* Target successfully performed drop action */
             ecore_x_selection_xdnd_clear();
             source->state = ECORE_X_DND_SOURCE_IDLE;
          }
        else if (source->version >= 5)
          {
             completed = EINA_FALSE;
             source->state = ECORE_X_DND_SOURCE_CONVERTING;

             /* FIXME: Probably need to add a timer to switch back to idle
              * and discard the selection data */
          }

        e = calloc(1, sizeof(Ecore_X_Event_Xdnd_Finished));
        if (!e) return;

        e->win = source->win;
        e->target = source->dest;
        e->completed = completed;
        if (source->version >= 5)
          {
             source->accepted_action = xevent->xclient.data.l[2];
             e->action = source->accepted_action;
          }
        else
          {
             source->accepted_action = 0;
             e->action = source->action;
          }

        ecore_event_add(ECORE_X_EVENT_XDND_FINISHED, e, NULL, NULL);
     }
   else if (xevent->xclient.message_type == ECORE_X_ATOM_NET_WM_STATE)
     {
        Ecore_X_Event_Window_State_Request *e;

        e = calloc(1, sizeof(Ecore_X_Event_Window_State_Request));
        if (!e) return;

        e->win = xevent->xclient.window;
        if (xevent->xclient.data.l[0] == 0)
          e->action = ECORE_X_WINDOW_STATE_ACTION_REMOVE;
        else if (xevent->xclient.data.l[0] == 1)
          e->action = ECORE_X_WINDOW_STATE_ACTION_ADD;
        else if (xevent->xclient.data.l[0] == 2)
          e->action = ECORE_X_WINDOW_STATE_ACTION_TOGGLE;
        else
          {
             free(e);
             return;
          }

        LOGFN;
        e->state[0] = _ecore_x_netwm_state_get(xevent->xclient.data.l[1]);
        if (e->state[0] == ECORE_X_WINDOW_STATE_UNKNOWN)
          {
//	     char *name;
             LOGFN;

//	     name = XGetAtomName(_ecore_x_disp, xevent->xclient.data.l[1]);
//	     if (name) ERR("Unknown state: %s", name);
//	     XFree(name);
          }
        e->state[1] = _ecore_x_netwm_state_get(xevent->xclient.data.l[2]);
        if (e->state[1] == ECORE_X_WINDOW_STATE_UNKNOWN)
          {
//	     char *name;
             LOGFN;

//	     name = XGetAtomName(_ecore_x_disp, xevent->xclient.data.l[2]);
//	     if (name) ERR("Unknown state: %s", name);
//	     XFree(name);
          }

        e->source = xevent->xclient.data.l[3];

        ecore_event_add(ECORE_X_EVENT_WINDOW_STATE_REQUEST, e, NULL, NULL);
     }
   else if ((xevent->xclient.message_type == ECORE_X_ATOM_WM_CHANGE_STATE)
            && (xevent->xclient.format == 32)
            && (xevent->xclient.data.l[0] == IconicState))
     {
        Ecore_X_Event_Window_State_Request *e;

        e = calloc(1, sizeof(Ecore_X_Event_Window_State_Request));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->action = ECORE_X_WINDOW_STATE_ACTION_ADD;
        e->state[0] = ECORE_X_WINDOW_STATE_ICONIFIED;

        ecore_event_add(ECORE_X_EVENT_WINDOW_STATE_REQUEST, e, NULL, NULL);
     }
   else if ((xevent->xclient.message_type == ECORE_X_ATOM_NET_WM_DESKTOP)
            && (xevent->xclient.format == 32))
     {
        Ecore_X_Event_Desktop_Change *e;

        e = calloc(1, sizeof(Ecore_X_Event_Desktop_Change));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->desk = xevent->xclient.data.l[0];
        e->source = xevent->xclient.data.l[1];

        ecore_event_add(ECORE_X_EVENT_DESKTOP_CHANGE, e, NULL, NULL);
     }
   else if (xevent->xclient.message_type ==
            ECORE_X_ATOM_NET_REQUEST_FRAME_EXTENTS)
     {
        Ecore_X_Event_Frame_Extents_Request *e;

        e = calloc(1, sizeof(Ecore_X_Event_Frame_Extents_Request));
        if (!e)
          return;

        e->win = xevent->xclient.window;

        ecore_event_add(ECORE_X_EVENT_FRAME_EXTENTS_REQUEST, e, NULL, NULL);
     }
   else if ((xevent->xclient.message_type == ECORE_X_ATOM_WM_PROTOCOLS)
            && ((Ecore_X_Atom)xevent->xclient.data.l[0] ==
                ECORE_X_ATOM_NET_WM_PING)
            && (xevent->xclient.format == 32))
     {
        Ecore_X_Event_Ping *e;
        Ecore_X_Window root = 0;

        e = calloc(1, sizeof(Ecore_X_Event_Ping));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->time = xevent->xclient.data.l[1];
        e->event_win = xevent->xclient.data.l[2];

        /* send a reply anyway - we are alive... eventloop at least */
        ecore_event_add(ECORE_X_EVENT_PING, e, NULL, NULL);
        if (ScreenCount(_ecore_x_disp) > 1)
          {
             LOGFN;
             root = ecore_x_window_root_get(e->win);
          }
        else
          root = DefaultRootWindow(_ecore_x_disp);

        if (xevent->xclient.window != root)
          {
             xevent->xclient.window = root;
             XSendEvent(_ecore_x_disp, root, False,
                        SubstructureRedirectMask | SubstructureNotifyMask,
                        xevent);
             if (_ecore_xlib_sync) ecore_x_sync();
          }
     }
   else if ((xevent->xclient.message_type ==
             ECORE_X_ATOM_NET_STARTUP_INFO_BEGIN) &&
            (xevent->xclient.format == 8))
     _ecore_x_netwm_startup_info_begin(xevent->xclient.window,
                                       xevent->xclient.data.b);
   else if ((xevent->xclient.message_type == ECORE_X_ATOM_NET_STARTUP_INFO) &&
            (xevent->xclient.format == 8))
     _ecore_x_netwm_startup_info(xevent->xclient.window,
                                 xevent->xclient.data.b);
   else if ((xevent->xclient.message_type == 27777)
            && (xevent->xclient.data.l[0] == 0x7162534)
            && (xevent->xclient.format == 32)
            && (xevent->xclient.window == _ecore_x_private_win))
     {
        int val = xevent->xclient.data.l[1] & 0xff;
        int anymod = (xevent->xclient.data.l[1] >> 8) & 0xff;
        int mod = xevent->xclient.data.l[4];
        int b = xevent->xclient.data.l[3];
        Ecore_X_Window swin = xevent->xclient.data.l[2];

        /* a grab sync marker */
        if (val == 1)
          {
             _ecore_x_window_grab_remove(swin, b, mod, anymod);
          }
        else if (val == 2)
          {
             const char *str;

             str = ecore_x_keysym_string_get(b);
             if (str) _ecore_x_key_grab_remove(swin, str, mod, anymod);
          }
     }
   else
     {
        Ecore_X_Event_Client_Message *e;
        int i;

        e = calloc(1, sizeof(Ecore_X_Event_Client_Message));
        if (!e)
          return;

        e->win = xevent->xclient.window;
        e->message_type = xevent->xclient.message_type;
        e->format = xevent->xclient.format;
        e->time = _ecore_x_event_last_time;
        for (i = 0; i < 5; i++)
          e->data.l[i] = xevent->xclient.data.l[i];

        ecore_event_add(ECORE_X_EVENT_CLIENT_MESSAGE, e, NULL, NULL);
     }
}

void
_ecore_x_event_handle_mapping_notify(XEvent *xevent)
{
   Ecore_X_Event_Mapping_Change *e;
   static unsigned long last_serial;
   int type;

   _ecore_x_last_event_mouse_move = EINA_FALSE;

   switch (xevent->xmapping.request)
     {
      case MappingModifier:
        type = ECORE_X_MAPPING_MODIFIER;
        break;

      case MappingKeyboard:
        if ((last_serial && (xevent->xmapping.serial == last_serial))) return;
        type = ECORE_X_MAPPING_KEYBOARD;
        last_serial = xevent->xmapping.serial;
        break;

      case MappingPointer:
      default:
        type = ECORE_X_MAPPING_MOUSE;
        break;
     }

   _ecore_x_window_grab_suspend();
   _ecore_x_key_grab_suspend();

   XRefreshKeyboardMapping((XMappingEvent *)xevent);
   _ecore_x_modifiers_get();

   _ecore_x_window_grab_resume();
   _ecore_x_key_grab_resume();
   e = calloc(1, sizeof(Ecore_X_Event_Mapping_Change));
   if (!e) return;
   e->type = type;
   e->keycode = xevent->xmapping.first_keycode;
   e->num = xevent->xmapping.count;
   ecore_event_add(ECORE_X_EVENT_MAPPING_CHANGE, e, NULL, NULL);
}

void
_ecore_x_event_handle_shape_change(XEvent *xevent)
{
   XShapeEvent *shape_event;
   Ecore_X_Event_Window_Shape *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   shape_event = (XShapeEvent *)xevent;
   e = calloc(1, sizeof(Ecore_X_Event_Window_Shape));
   if (!e)
     return;

   e->win = shape_event->window;
   e->time = shape_event->time;
   switch (shape_event->kind)
     {
      case ShapeBounding:
        e->type = ECORE_X_SHAPE_BOUNDING;
        break;

      case ShapeClip:
        e->type = ECORE_X_SHAPE_CLIP;
        break;

      case ShapeInput:
        e->type = ECORE_X_SHAPE_INPUT;
        break;

      default:
        break;
     }
   e->x = shape_event->x;
   e->y = shape_event->y;
   e->w = shape_event->width;
   e->h = shape_event->height;
   e->shaped = shape_event->shaped;
   ecore_event_add(ECORE_X_EVENT_WINDOW_SHAPE, e, NULL, NULL);
}

void
_ecore_x_event_handle_screensaver_notify(XEvent *xevent)
{
#ifdef ECORE_XSS
   XScreenSaverNotifyEvent *screensaver_event;
   Ecore_X_Event_Screensaver_Notify *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   screensaver_event = (XScreenSaverNotifyEvent *)xevent;
   e = calloc(1, sizeof(Ecore_X_Event_Screensaver_Notify));
   if (!e)
     return;

   e->win = screensaver_event->window;
   if ((screensaver_event->state == ScreenSaverOn) ||
       (screensaver_event->state == ScreenSaverCycle))
     e->on = EINA_TRUE;
   else
     e->on = EINA_FALSE;

   e->time = screensaver_event->time;
   ecore_event_add(ECORE_X_EVENT_SCREENSAVER_NOTIFY, e, NULL, NULL);
#else /* ifdef ECORE_XSS */
   (void) xevent;
#endif /* ifdef ECORE_XSS */
}

void
_ecore_x_event_handle_sync_counter(XEvent *xevent)
{
   XSyncCounterNotifyEvent *sync_counter_event;
   Ecore_X_Event_Sync_Counter *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   sync_counter_event = (XSyncCounterNotifyEvent *)xevent;
   e = calloc(1, sizeof(Ecore_X_Event_Sync_Counter));
   if (!e)
     return;

   e->time = sync_counter_event->time;
   ecore_event_add(ECORE_X_EVENT_SYNC_COUNTER, e, NULL, NULL);
}

void
_ecore_x_event_handle_sync_alarm(XEvent *xevent)
{
   XSyncAlarmNotifyEvent *sync_alarm_event;
   Ecore_X_Event_Sync_Alarm *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   sync_alarm_event = (XSyncAlarmNotifyEvent *)xevent;

   e = calloc(1, sizeof(Ecore_X_Event_Sync_Alarm));
   if (!e)
     return;

   e->time = sync_alarm_event->time;
   e->alarm = sync_alarm_event->alarm;
   ecore_event_add(ECORE_X_EVENT_SYNC_ALARM, e, NULL, NULL);
}

#ifdef ECORE_XRANDR
void
_ecore_x_event_handle_randr_change(XEvent *xevent)
{
   XRRScreenChangeNotifyEvent *randr_event;
   Ecore_X_Event_Screen_Change *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   randr_event = (XRRScreenChangeNotifyEvent *)xevent;
   if (!XRRUpdateConfiguration(xevent))
     ERR("Can't update RR config!");

   e = calloc(1, sizeof(Ecore_X_Event_Screen_Change));
   if (!e)
     return;

   e->win = randr_event->window;
   e->root = randr_event->root;
   e->size.width = randr_event->width;
   e->size.height = randr_event->height;
   e->time = randr_event->timestamp;
   e->config_time = randr_event->config_timestamp;
   e->size.width_mm = randr_event->mwidth;
   e->size.height_mm = randr_event->mheight;
   e->orientation = randr_event->rotation;
   e->subpixel_order = randr_event->subpixel_order;
   ecore_event_add(ECORE_X_EVENT_SCREEN_CHANGE, e, NULL, NULL);
}

static void
_ecore_x_event_handle_randr_notify_crtc_change(const XRRNotifyEvent *xevent)
{
   const XRRCrtcChangeNotifyEvent *randr_event;
   Ecore_X_Event_Randr_Crtc_Change *e;

   randr_event = (const XRRCrtcChangeNotifyEvent *)xevent;

   e = calloc(1, sizeof(Ecore_X_Event_Randr_Crtc_Change));
   if (!e)
     return;

   e->win = randr_event->window;
   e->crtc = randr_event->crtc;
   e->mode = randr_event->mode;
   e->orientation = randr_event->rotation;
   e->geo.x = randr_event->x;
   e->geo.y = randr_event->y;
   e->geo.w = randr_event->width;
   e->geo.h = randr_event->height;
   ecore_event_add(ECORE_X_EVENT_RANDR_CRTC_CHANGE, e, NULL, NULL);
}

static void
_ecore_x_event_handle_randr_notify_output_change(const XRRNotifyEvent *xevent)
{
   const XRROutputChangeNotifyEvent *randr_event;
   Ecore_X_Event_Randr_Output_Change *e;

   randr_event = (const XRROutputChangeNotifyEvent *)xevent;

   e = calloc(1, sizeof(Ecore_X_Event_Randr_Output_Change));
   if (!e)
     return;

   e->win = randr_event->window;
   e->output = randr_event->output;
   e->crtc = randr_event->crtc;
   e->mode = randr_event->mode;
   e->orientation = randr_event->rotation;
   e->connection = randr_event->connection;
   e->subpixel_order = randr_event->subpixel_order;
   ecore_event_add(ECORE_X_EVENT_RANDR_OUTPUT_CHANGE, e, NULL, NULL);
}

static void
_ecore_x_event_handle_randr_notify_output_property(const XRRNotifyEvent *xevent)
{
   const XRROutputPropertyNotifyEvent *randr_event;
   Ecore_X_Event_Randr_Output_Property_Notify *e;

   randr_event = (const XRROutputPropertyNotifyEvent *)xevent;

   e = calloc(1, sizeof(Ecore_X_Event_Randr_Output_Property_Notify));
   if (!e)
     return;

   e->win = randr_event->window;
   e->output = randr_event->output;
   e->property = randr_event->property;
   e->time = randr_event->timestamp;
   if (randr_event->state == PropertyNewValue)
     e->state = ECORE_X_RANDR_PROPERTY_CHANGE_ADD;
   else
     e->state = ECORE_X_RANDR_PROPERTY_CHANGE_DEL;
   ecore_event_add(ECORE_X_EVENT_RANDR_OUTPUT_PROPERTY_NOTIFY, e, NULL, NULL);
}

void
_ecore_x_event_handle_randr_notify(XEvent *xevent)
{
   const XRRNotifyEvent *randr_event;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   randr_event = (const XRRNotifyEvent *)xevent;
   LOGFN;
   switch (randr_event->subtype)
     {
      case RRNotify_CrtcChange:
        _ecore_x_event_handle_randr_notify_crtc_change(randr_event);
        break;

      case RRNotify_OutputChange:
        _ecore_x_event_handle_randr_notify_output_change(randr_event);
        break;

      case RRNotify_OutputProperty:
        _ecore_x_event_handle_randr_notify_output_property(randr_event);
        break;

      default:
        ERR("Unknown XRandR RRNotify subtype: %d.",
            randr_event->subtype);
        break;
     }
}

#endif /* ifdef ECORE_XRANDR */

#ifdef ECORE_XFIXES
void
_ecore_x_event_handle_fixes_selection_notify(XEvent *event)
{
   XFixesSelectionNotifyEvent *notify_event =
     (XFixesSelectionNotifyEvent *)event;
   Ecore_X_Event_Fixes_Selection_Notify *e;
   Ecore_X_Atom sel;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   /* Nothing here yet */

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

   e->win = notify_event->window;
   e->owner = notify_event->owner;
   e->time = notify_event->timestamp;
   e->selection_time = notify_event->selection_timestamp;
   e->atom = sel = notify_event->selection;
   if (sel == ECORE_X_ATOM_SELECTION_PRIMARY)
     e->selection = ECORE_X_SELECTION_PRIMARY;
   else if (sel == ECORE_X_ATOM_SELECTION_SECONDARY)
     e->selection = ECORE_X_SELECTION_SECONDARY;
   else if (sel == ECORE_X_ATOM_SELECTION_XDND)
     e->selection = ECORE_X_SELECTION_XDND;
   else if (sel == ECORE_X_ATOM_SELECTION_CLIPBOARD)
     e->selection = ECORE_X_SELECTION_CLIPBOARD;
   else
     e->selection = ECORE_X_SELECTION_OTHER;
   e->reason = notify_event->subtype;

   ecore_event_add(ECORE_X_EVENT_FIXES_SELECTION_NOTIFY, e, NULL, NULL);
}

#endif /* ifdef ECORE_XFIXES */

#ifdef ECORE_XDAMAGE
void
_ecore_x_event_handle_damage_notify(XEvent *event)
{
   XDamageNotifyEvent *damage_event;
   Ecore_X_Event_Damage *e;

   _ecore_x_last_event_mouse_move = EINA_FALSE;
   damage_event = (XDamageNotifyEvent *)event;

   e = calloc(1, sizeof(Ecore_X_Event_Damage));
   if (!e)
     return;

   e->level = damage_event->level;
   e->drawable = damage_event->drawable;
   e->damage = damage_event->damage;
   e->more = damage_event->more;
   e->time = damage_event->timestamp;
   e->area.x = damage_event->area.x;
   e->area.y = damage_event->area.y;
   e->area.width = damage_event->area.width;
   e->area.height = damage_event->area.height;
   e->geometry.x = damage_event->geometry.x;
   e->geometry.y = damage_event->geometry.y;
   e->geometry.width = damage_event->geometry.width;
   e->geometry.height = damage_event->geometry.height;

   ecore_event_add(ECORE_X_EVENT_DAMAGE_NOTIFY, e, NULL, NULL);
}

#endif /* ifdef ECORE_XDAMAGE */

static void
_ecore_x_event_free_generic_event(void *data,
                                  void *ev)
{
   Ecore_X_Event_Generic *e = (Ecore_X_Event_Generic *)ev;

   if (data)
     {
        if (e->data)
          XFreeEventData(_ecore_x_disp, (XGenericEventCookie *)data);
        free(data);
     }
   free(e);
}

void
_ecore_x_event_handle_generic_event(XEvent *event)
{
   XGenericEvent *generic_event;
   Ecore_X_Event_Generic *e;
   XGenericEventCookie *data;

   LOGFN;
   generic_event = (XGenericEvent *)event;

#ifdef ECORE_XPRESENT
   if (generic_event->extension == _ecore_x_present_major)
     {
        _ecore_x_present_handler(generic_event);
        return;
     }
#endif

   e = calloc(1, sizeof(Ecore_X_Event_Generic));
   if (!e)
     return;

   if (XGetEventData(_ecore_x_disp, &(event->xcookie)))
     {
        e->cookie = event->xcookie.cookie;
        e->data = event->xcookie.data;
     }
   else
     {
        e->cookie = 0;
        e->data = NULL;
     }

   e->extension = generic_event->extension;
   e->evtype = generic_event->evtype;
#ifdef ECORE_XI2
   if (e->extension == _ecore_x_xi2_opcode)
     _ecore_x_input_handler(event);
#endif /* ifdef ECORE_XI2 */
   data = malloc(sizeof(XGenericEventCookie));
   if (data) memcpy(data, &(event->xcookie), sizeof(XGenericEventCookie));
   ecore_event_add(ECORE_X_EVENT_GENERIC,
                   e,
                   _ecore_x_event_free_generic_event,
                   data);
}

#ifdef ECORE_XKB

void
free_hash(void *data, void *ev)
{
   eina_hash_del_by_key(emitted_events, (void *)&data);
   if (ev) free(ev);
}

void
_ecore_x_event_handle_xkb(XEvent *xevent)
{
   XkbEvent *xkbev = (XkbEvent *)xevent;
   Ecore_X_Event_Xkb *e;

   if (xkbev->any.xkb_type == XkbStateNotify)
     {
        if (eina_hash_find(emitted_events, &xkbev->state.serial)) return;

        e = calloc(1, sizeof(Ecore_X_Event_Xkb));
        if (!e) return;

        e->group = xkbev->state.group;
        e->base_group = xkbev->state.base_group;
        e->latched_group = xkbev->state.latched_group;
        e->locked_group = xkbev->state.locked_group;

        e->mods = xkbev->state.mods;
        e->base_mods = xkbev->state.base_mods;
        e->latched_mods = xkbev->state.latched_mods;
        e->locked_mods = xkbev->state.locked_mods;
        ecore_event_add(ECORE_X_EVENT_XKB_STATE_NOTIFY, e, free_hash,
                        (void *)(intptr_t)xkbev->new_kbd.serial);
        eina_hash_add(emitted_events, &xkbev->state.serial, (void *)1);
     }
   else if ((xkbev->any.xkb_type == XkbNewKeyboardNotify) ||
            (xkbev->any.xkb_type == XkbMapNotify))
     {
        if (eina_hash_find(emitted_events, &xkbev->state.serial)) return;

        e = calloc(1, sizeof(Ecore_X_Event_Xkb));
        if (!e) return;

        if (xkbev->any.xkb_type == XkbMapNotify)
          {
             XkbMapNotifyEvent *xkbmapping = (XkbMapNotifyEvent *)xkbev;

             _ecore_x_window_grab_suspend();
             _ecore_x_key_grab_suspend();
             XkbGetMap(_ecore_x_disp, XkbAllMapComponentsMask,
                       xkbmapping->device);
             XkbRefreshKeyboardMapping(xkbmapping);
             _ecore_x_modifiers_get();
             _ecore_x_window_grab_resume();
             _ecore_x_key_grab_resume();
             e->map_notify = EINA_TRUE;
          }
        else
          {
             XkbNewKeyboardNotifyEvent *xkbnkn = (void*)xkbev;
             if (!(xkbnkn->changed & XkbNKN_KeycodesMask)) return;
          }
        ecore_event_add(ECORE_X_EVENT_XKB_NEWKBD_NOTIFY, e, free_hash,
                        (void *)(intptr_t)xkbev->new_kbd.serial);
        eina_hash_add(emitted_events, &xkbev->new_kbd.serial, (void *)1);
     }
}
#endif /* ifdef ECORE_XKB */
