#include "e.h"
#include "e_comp_wl.h"
#include "e_surface.h"
#include <sys/mman.h>

/* compositor function prototypes */
static void _seat_send_updated_caps(struct wl_seat *seat);
static void _move_resources(struct wl_list *dest, struct wl_list *src);
static void _move_resources_for_client(struct wl_list *dest, struct wl_list *src, struct wl_client *client);

static struct wl_resource *_find_resource_for_surface(struct wl_list *list, struct wl_resource *surface);

static void _default_grab_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y);
static void _default_grab_motion(struct wl_pointer_grab *grab, uint32_t timestamp, wl_fixed_t x, wl_fixed_t y);
static void _default_grab_button(struct wl_pointer_grab *grab, uint32_t timestamp, uint32_t button, uint32_t state_w);

static void _default_grab_touch_down(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id, wl_fixed_t sx, wl_fixed_t sy);
static void _default_grab_touch_up(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id);
static void _default_grab_touch_motion(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id, wl_fixed_t sx, wl_fixed_t sy);

static void _data_offer_accept(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial, const char *mime_type);
static void _data_offer_receive(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *mime_type, int32_t fd);
static void _data_offer_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource);

static void _destroy_data_offer(struct wl_resource *resource);
static void _destroy_offer_data_source(struct wl_listener *listener, void *data EINA_UNUSED);

static void _create_data_source(struct wl_client *client, struct wl_resource *resource, uint32_t id);
static void _get_data_device(struct wl_client *client, struct wl_resource *manager_resource EINA_UNUSED, uint32_t id, struct wl_resource *seat_resource);

static void _current_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED);
static void _bind_manager(struct wl_client *client, void *data EINA_UNUSED, uint32_t version EINA_UNUSED, uint32_t id);

static void _default_grab_key(struct wl_keyboard_grab *grab, uint32_t timestamp, uint32_t key, uint32_t state);
static void _default_grab_modifiers(struct wl_keyboard_grab *grab, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group);

static void _data_device_start_drag(struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, struct wl_resource *origin_resource EINA_UNUSED, struct wl_resource *icon_resource, uint32_t serial EINA_UNUSED);
static void _data_device_set_selection(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial);
static void _destroy_data_device_icon(struct wl_listener *listener, void *data EINA_UNUSED);

static void _destroy_selection_data_source(struct wl_listener *listener, void *data EINA_UNUSED);
static void _data_source_offer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *type);
static void _data_source_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource);
static void _destroy_data_source(struct wl_resource *resource);
static void _destroy_data_device_source(struct wl_listener *listener, void *data EINA_UNUSED);
static void _data_device_end_drag_grab(struct wl_seat *seat);

static void _drag_grab_button(struct wl_pointer_grab *grab, uint32_t timestamp EINA_UNUSED, uint32_t button, uint32_t state_w);
static void _drag_grab_motion(struct wl_pointer_grab *grab, uint32_t timestamp, wl_fixed_t x, wl_fixed_t y);

static void _destroy_drag_focus(struct wl_listener *listener, void *data EINA_UNUSED);
static void _drag_grab_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y);

static void _client_source_accept(struct wl_data_source *source, uint32_t timestamp EINA_UNUSED, const char *mime_type);
static void _client_source_send(struct wl_data_source *source, const char *mime_type, int32_t fd);
static void _client_source_cancel(struct wl_data_source *source);

static void _e_comp_wl_cb_bind(struct wl_client *client, void *data, unsigned int version, unsigned int id);
static Eina_Bool _e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdl EINA_UNUSED);
static Eina_Bool _e_comp_wl_cb_idle(void *data EINA_UNUSED);
static Eina_Bool _e_comp_wl_cb_module_idle(void *data EINA_UNUSED);
static Eina_Bool _e_comp_wl_cb_keymap_changed(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED);

/* compositor interface prototypes */
static void _e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_cb_surface_destroy(struct wl_resource *resource);
static void _e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_cb_region_destroy(struct wl_resource *resource);

/* input function prototypes */
static Eina_Bool _e_comp_wl_input_init(void);
static void _e_comp_wl_input_shutdown(void);
static void _e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version EINA_UNUSED, unsigned int id);
static void _e_comp_wl_input_cb_unbind(struct wl_resource *resource);
static struct xkb_keymap *_e_comp_wl_input_keymap_get(void);
static int _e_comp_wl_input_keymap_fd_get(off_t size);
static E_Wayland_Keyboard_Info *_e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap);

/* input interface prototypes */
static void _e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);
static void _e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id);

/* pointer function prototypes */
static void _e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED);
static void _e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h);
static void _e_comp_wl_pointer_unmap(E_Wayland_Surface *ews);

/* pointer interface prototypes */
static void _e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y);
static void _e_comp_wl_pointer_cb_release(struct wl_client *client, struct wl_resource *resource);

/* keyboard interface prototypes */
static void _e_comp_wl_keyboard_cb_release(struct wl_client *client, struct wl_resource *resource);

/* touch interface prototypes */
static void _e_comp_wl_touch_cb_release(struct wl_client *client, struct wl_resource *resource);

/* region interface prototypes */
static void _e_comp_wl_region_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource);
static void _e_comp_wl_region_cb_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h);
static void _e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h);

/* surface function prototypes */
static void _e_comp_wl_surface_cb_pending_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED);
static void _e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource);
static void _e_comp_wl_surface_buffer_reference(E_Wayland_Buffer_Reference *ref, E_Wayland_Buffer *buffer);
static void _e_comp_wl_surface_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED);
static E_Wayland_Buffer *_e_comp_wl_surface_buffer_resource(struct wl_resource *resource);
static void _e_comp_wl_surface_buffer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED);

/* surface interface prototypes */
static void _e_comp_wl_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource);
static void _e_comp_wl_surface_cb_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int x, int y);
static void _e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h);
static void _e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, unsigned int callback);
static void _e_comp_wl_surface_cb_opaque_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource);
static void _e_comp_wl_surface_cb_input_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource);
static void _e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource);
static void _e_comp_wl_surface_cb_buffer_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int transform EINA_UNUSED);
static void _e_comp_wl_surface_cb_buffer_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int scale EINA_UNUSED);

/* local wayland interfaces */
static const struct wl_compositor_interface _e_compositor_interface = 
{
   _e_comp_wl_cb_surface_create,
   _e_comp_wl_cb_region_create
};

static const struct wl_seat_interface _e_input_interface = 
{
   _e_comp_wl_input_cb_pointer_get,
   _e_comp_wl_input_cb_keyboard_get,
   _e_comp_wl_input_cb_touch_get,
};

static const struct wl_pointer_interface _e_pointer_interface = 
{
   _e_comp_wl_pointer_cb_cursor_set,
   _e_comp_wl_pointer_cb_release
};

static const struct wl_keyboard_interface _e_keyboard_interface = 
{
   _e_comp_wl_keyboard_cb_release
};

static const struct wl_touch_interface _e_touch_interface = 
{
   _e_comp_wl_touch_cb_release
};

static const struct wl_region_interface _e_region_interface = 
{
   _e_comp_wl_region_cb_destroy,
   _e_comp_wl_region_cb_add,
   _e_comp_wl_region_cb_subtract
};

static const struct wl_surface_interface _e_surface_interface = 
{
   _e_comp_wl_surface_cb_destroy,
   _e_comp_wl_surface_cb_attach,
   _e_comp_wl_surface_cb_damage,
   _e_comp_wl_surface_cb_frame,
   _e_comp_wl_surface_cb_opaque_region_set,
   _e_comp_wl_surface_cb_input_region_set,
   _e_comp_wl_surface_cb_commit,
   _e_comp_wl_surface_cb_buffer_transform_set,
   _e_comp_wl_surface_cb_buffer_scale_set
};

static const struct wl_pointer_grab_interface _e_pointer_grab_interface = 
{
   _default_grab_focus,
   _default_grab_motion,
   _default_grab_button
};

static const struct wl_keyboard_grab_interface _e_default_keyboard_grab_interface = 
{
   _default_grab_key,
   _default_grab_modifiers,
};

static const struct wl_data_offer_interface _e_data_offer_interface = 
{
   _data_offer_accept,
   _data_offer_receive,
   _data_offer_destroy,
};

static const struct wl_data_device_manager_interface _e_manager_interface = 
{
   _create_data_source,
   _get_data_device
};

static const struct wl_data_device_interface _e_data_device_interface = 
{
   _data_device_start_drag,
   _data_device_set_selection,
};

static const struct wl_touch_grab_interface _e_default_touch_grab_interface = 
{
   _default_grab_touch_down,
   _default_grab_touch_up,
   _default_grab_touch_motion
};

static struct wl_data_source_interface _e_data_source_interface = 
{
   _data_source_offer,
   _data_source_destroy
};

static const struct wl_pointer_grab_interface _e_drag_grab_interface = 
{
   _drag_grab_focus,
   _drag_grab_motion,
   _drag_grab_button,
};

/* local variables */
static Ecore_Idler *_module_idler = NULL;

static Eina_Hash *_e_wl_border_hash = NULL;

/* external variables */
EAPI E_Wayland_Compositor *_e_wl_comp;

/* external functions */
EINTERN Eina_Bool 
e_comp_wl_init(void)
{
   int fd = 0;

   /* try to allocate space for a new compositor */
   if (!(_e_wl_comp = E_NEW(E_Wayland_Compositor, 1)))
     return EINA_FALSE;

   /* try to create a wayland display */
   if (!(_e_wl_comp->wl.display = wl_display_create()))
     {
        ERR("Could not create a Wayland Display: %m");
        goto err;
     }

   /* init compositor signals */
   wl_signal_init(&_e_wl_comp->signals.destroy);
   wl_signal_init(&_e_wl_comp->signals.activate);
   wl_signal_init(&_e_wl_comp->signals.kill);
   wl_signal_init(&_e_wl_comp->signals.seat);

   /* try to add compositor to the displays globals */
   if (!wl_global_create(_e_wl_comp->wl.display, &wl_compositor_interface, 3, 
                         _e_wl_comp, _e_comp_wl_cb_bind))
     {
        ERR("Could not add compositor to globals: %m");
        goto err;
     }

   /* init data device manager */
   wl_data_device_manager_init(_e_wl_comp->wl.display);

   /* try to init shm mechanism */
   if (wl_display_init_shm(_e_wl_comp->wl.display) < 0)
     ERR("Could not initialize SHM: %m");

#ifdef HAVE_WAYLAND_EGL
   /* try to get the egl display
    * 
    * NB: This is interesting....if we try to eglGetDisplay and pass in the 
    * wayland display, then EGL fails due to XCB not owning the event queue.
    * If we pass it a NULL, it inits just fine */
   _e_wl_comp->egl.display = eglGetDisplay((EGLNativeDisplayType)ecore_x_display_get());
   if (_e_wl_comp->egl.display == EGL_NO_DISPLAY)
     ERR("Could not get EGL display: %m");
   else
     {
        EGLint major, minor;

        /* try to initialize egl */
        if (!eglInitialize(_e_wl_comp->egl.display, &major, &minor))
          {
             ERR("Could not initialize EGL: %m");
             eglTerminate(_e_wl_comp->egl.display);
          }
        else
          {
             EGLint n;
             EGLint attribs[] = 
               {
                  EGL_SURFACE_TYPE, EGL_WINDOW_BIT, 
                  EGL_RED_SIZE, 1, EGL_GREEN_SIZE, 1, EGL_BLUE_SIZE, 1, 
                  EGL_ALPHA_SIZE, 1, EGL_RENDERABLE_TYPE, 
                  EGL_OPENGL_ES2_BIT, EGL_NONE
               };
             /* const char *exts; */

             if ((!eglChooseConfig(_e_wl_comp->egl.display, attribs, 
                                   &_e_wl_comp->egl.config, 1, &n) || (n == 0)))
               {
                  ERR("Could not choose EGL config: %m");
                  eglTerminate(_e_wl_comp->egl.display);
               }

             /* if (!eglBindAPI(EGL_OPENGL_ES_API)) */
             /*   { */
             /*      ERR("Could not bind EGL API: %m"); */
             /*      eglTerminate(_e_wl_comp->egl.display); */
             /*   } */

             /* exts = (const char *)eglQueryString(_e_wl_comp->egl.display, EGL_EXTENSIONS); */
             /* if (!exts) */
             /*   { */
             /*      ERR("Could not get EGL Extensions: %m"); */
             /*      eglTerminate(_e_wl_comp->egl.display); */
             /*   } */
             /* else */
             /*   { */
             /*      if (strstr(exts, "EGL_WL_bind_wayland_display")) */
             /*        { */
             /*           _e_wl_comp->egl.bind_display =  */
             /*             (void *)eglGetProcAddress("eglBindWaylandDisplayWL"); */
             /*           _e_wl_comp->egl.unbind_display =  */
             /*             (void *)eglGetProcAddress("eglUnbindWaylandDisplayWL"); */
             /*        } */
             /*   } */

             /* if (_e_wl_comp->egl.bind_display) */
             /*   { */
             /*      EGLBoolean ret; */

             /*      ret = _e_wl_comp->egl.bind_display(_e_wl_comp->egl.display,  */
             /*                                         _e_wl_comp->wl.display); */
             /*      if (!ret) */
             /*        { */
             /*           ERR("Could not bind EGL Wayland Display: %m"); */
             /*           _e_wl_comp->egl.bound = EINA_FALSE; */
             /*        } */
             /*      else */
             /*        _e_wl_comp->egl.bound = EINA_TRUE; */
             /*   } */
          }
     }
#endif

   /* try to initialize input */
   if (!_e_comp_wl_input_init())
     {
        ERR("Could not initialize input: %m");
        goto err;
     }

   /* setup keymap_change event handler */
   _e_wl_comp->kbd_handler = 
     ecore_event_handler_add(ECORE_X_EVENT_XKB_STATE_NOTIFY, 
                             _e_comp_wl_cb_keymap_changed, NULL);

   /* get the displays event loop */
   _e_wl_comp->wl.loop = wl_display_get_event_loop(_e_wl_comp->wl.display);

   /* get the event loop's file descriptor so we can listen on it */
   fd = wl_event_loop_get_fd(_e_wl_comp->wl.loop);

   /* add the fd to E's main loop */
   _e_wl_comp->fd_handler = 
     ecore_main_fd_handler_add(fd, ECORE_FD_READ, 
                               _e_comp_wl_cb_read, NULL, NULL, NULL);

   /* add an idler for flushing clients */
   _e_wl_comp->idler = ecore_idle_enterer_add(_e_comp_wl_cb_idle, NULL);

   /* TODO: event handlers ?? */

   /* try to add a display socket */
   if (wl_display_add_socket(_e_wl_comp->wl.display, NULL) < 0)
     {
        ERR("Could not add a Wayland Display socket: %m");
        goto err;
     }

   /* add an idler for deferred shell module loading */
   _module_idler = ecore_idler_add(_e_comp_wl_cb_module_idle, NULL);

   /* return success */
   return EINA_TRUE;

err:
   /* remove kbd handler */
   if (_e_wl_comp->kbd_handler) 
     ecore_event_handler_del(_e_wl_comp->kbd_handler);

   /* remove the module idler */
   if (_module_idler) ecore_idler_del(_module_idler);

#ifdef HAVE_WAYLAND_EGL
   /* unbind wayland display */
   if (_e_wl_comp->egl.bound)
     _e_wl_comp->egl.unbind_display(_e_wl_comp->egl.display, _e_wl_comp->wl.display);

   /* terminate the egl display */
   if (_e_wl_comp->egl.display) eglTerminate(_e_wl_comp->egl.display);

   eglReleaseThread();
#endif

   /* if we have a display, destroy it */
   if (_e_wl_comp->wl.display) wl_display_destroy(_e_wl_comp->wl.display);

   /* free the compositor */
   E_FREE(_e_wl_comp);

   /* return failure */
   return EINA_FALSE;
}

EINTERN void 
e_comp_wl_shutdown(void)
{
   E_Module *mod = NULL;

   if (_e_wl_comp)
     {
        /* remove the idler */
        if (_e_wl_comp->idler) ecore_idler_del(_e_wl_comp->idler);

        /* remove the fd handler */
        if (_e_wl_comp->fd_handler)
          ecore_main_fd_handler_del(_e_wl_comp->fd_handler);

        /* shutdown input */
        _e_comp_wl_input_shutdown();

#ifdef HAVE_WAYLAND_EGL
        /* unbind wayland display */
        if (_e_wl_comp->egl.bound)
          _e_wl_comp->egl.unbind_display(_e_wl_comp->egl.display, _e_wl_comp->wl.display);

        /* terminate the egl display */
        if (_e_wl_comp->egl.display) eglTerminate(_e_wl_comp->egl.display);

        eglReleaseThread();
#endif

        /* if we have a display, destroy it */
        if (_e_wl_comp->wl.display) wl_display_destroy(_e_wl_comp->wl.display);

        /* free the compositor */
        E_FREE(_e_wl_comp);
     }

   E_FREE_FUNC(_e_wl_border_hash, eina_hash_free);

   /* disable the loaded shell module */
   /* TODO: we should have a config variable somewhere to store which 
    * shell we want to unload (tablet, mobile, etc) */
   if ((mod = e_module_find("wl_desktop_shell")))
     e_module_disable(mod);
}

EAPI void
e_comp_wl_border_surface_add(Ecore_Window win, const E_Border *bd)
{
   if (!_e_wl_border_hash)
     _e_wl_border_hash = eina_hash_int32_new(NULL);
   eina_hash_add(_e_wl_border_hash, &win, bd);
}

EAPI void
e_comp_wl_border_surface_del(Ecore_Window win)
{
   if (!_e_wl_border_hash) return;
   eina_hash_del_by_key(_e_wl_border_hash, &win);
}

EAPI E_Border *
e_comp_wl_border_surface_find(Ecore_Window win)
{
   if (!_e_wl_border_hash) return NULL;
   return eina_hash_find(_e_wl_border_hash, &win);
}

EAPI void 
wl_seat_init(struct wl_seat *seat)
{
   memset(seat, 0, sizeof *seat);

   wl_signal_init(&seat->destroy_signal);

   seat->selection_data_source = NULL;
   wl_list_init(&seat->base_resource_list);
   wl_signal_init(&seat->selection_signal);
   wl_list_init(&seat->drag_resource_list);
   wl_signal_init(&seat->drag_icon_signal);
}

EAPI void 
wl_seat_release(struct wl_seat *seat)
{
   /* if (seat->pointer) */
   /*   { */
   /*      if (seat->pointer->focus) */
   /*        wl_list_remove(&seat->pointer->focus_listener.link); */
   /*   } */

   /* if (seat->keyboard) */
   /*   { */
   /*      if (seat->keyboard->focus) */
   /*        wl_list_remove(&seat->keyboard->focus_listener.link); */
   /*   } */

   /* if (seat->touch) */
   /*   { */
   /*      if (seat->touch->focus) */
   /*        wl_list_remove(&seat->touch->focus_listener.link); */
   /*   } */

   wl_signal_emit(&seat->destroy_signal, seat);
}

EAPI void 
wl_seat_set_pointer(struct wl_seat *seat, struct wl_pointer *pointer)
{
   if (pointer && (seat->pointer || pointer->seat)) return; /* XXX: error? */
   if (!pointer && !seat->pointer) return;

   seat->pointer = pointer;
   if (pointer) pointer->seat = seat;

   _seat_send_updated_caps(seat);
}

EAPI void 
wl_seat_set_keyboard(struct wl_seat *seat, struct wl_keyboard *keyboard)
{
   if (keyboard && (seat->keyboard || keyboard->seat)) return; /* XXX: error? */
   if (!keyboard && !seat->keyboard) return;

   seat->keyboard = keyboard;
   if (keyboard) keyboard->seat = seat;

   _seat_send_updated_caps(seat);
}

EAPI void 
wl_seat_set_touch(struct wl_seat *seat, struct wl_touch *touch)
{
   if (touch && (seat->touch || touch->seat)) return; /* XXX: error? */
   if (!touch && !seat->touch) return;

   seat->touch = touch;
   if (touch) touch->seat = seat;

   _seat_send_updated_caps(seat);
}

EAPI void 
wl_pointer_init(struct wl_pointer *pointer)
{
   memset(pointer, 0, sizeof *pointer);
   wl_list_init(&pointer->resource_list);
   wl_list_init(&pointer->focus_resource_list);
   pointer->default_grab.interface = &_e_pointer_grab_interface;
   pointer->default_grab.pointer = pointer;
   pointer->grab = &pointer->default_grab;
   wl_signal_init(&pointer->focus_signal);

   pointer->x = wl_fixed_from_int(100);
   pointer->y = wl_fixed_from_int(100);
}

EAPI void 
wl_pointer_set_focus(struct wl_pointer *pointer, struct wl_resource *surface, wl_fixed_t sx, wl_fixed_t sy)
{
   struct wl_keyboard *kbd = pointer->seat->keyboard;
   struct wl_resource *res;
   struct wl_list *lst;
   uint32_t serial;

   lst = &pointer->focus_resource_list;
   if ((!wl_list_empty(lst)) && (pointer->focus != surface))
     {
        serial = wl_display_next_serial(_e_wl_comp->wl.display);
        wl_resource_for_each(res, lst)
          wl_pointer_send_leave(res, serial, pointer->focus);

        _move_resources(&pointer->resource_list, lst);
     }

   if ((_find_resource_for_surface(&pointer->resource_list, surface)) && 
       (pointer->focus != surface))
     {
        struct wl_client *client;

        client = wl_resource_get_client(surface);
        _move_resources_for_client(lst, &pointer->resource_list, client);

        wl_resource_for_each(res, lst)
          wl_pointer_send_enter(res, serial, surface, sx, sy);

        pointer->focus_serial = serial;
     }

   if ((kbd) && (surface) && (kbd->focus != pointer->focus))
     {
        struct wl_client *client;

        client = wl_resource_get_client(surface);

        wl_resource_for_each(res, &kbd->resource_list)
          {
             if (wl_resource_get_client(res) == client)
               wl_keyboard_send_modifiers(res, serial,
                                          kbd->modifiers.mods_depressed,
                                          kbd->modifiers.mods_latched,
                                          kbd->modifiers.mods_locked,
                                          kbd->modifiers.group);
          }
     }

   pointer->focus = surface;
   pointer->default_grab.focus = surface;
   wl_signal_emit(&pointer->focus_signal, pointer);
}

EAPI void 
wl_pointer_start_grab(struct wl_pointer *pointer, struct wl_pointer_grab *grab)
{
   const struct wl_pointer_grab_interface *interface;

   pointer->grab = grab;
   interface = pointer->grab->interface;
   grab->pointer = pointer;

   if (pointer->current)
     interface->focus(pointer->grab, pointer->current,
                       pointer->current_x, pointer->current_y);
}

EAPI void 
wl_pointer_end_grab(struct wl_pointer *pointer)
{
   const struct wl_pointer_grab_interface *interface;

   pointer->grab = &pointer->default_grab;
   interface = pointer->grab->interface;
   interface->focus(pointer->grab, pointer->current,
                    pointer->current_x, pointer->current_y);
}

EAPI void 
wl_keyboard_init(struct wl_keyboard *keyboard)
{
   memset(keyboard, 0, sizeof *keyboard);
   wl_list_init(&keyboard->resource_list);
   wl_array_init(&keyboard->keys);
   wl_list_init(&keyboard->focus_resource_list);
   keyboard->default_grab.interface = &_e_default_keyboard_grab_interface;
   keyboard->default_grab.keyboard = keyboard;
   keyboard->grab = &keyboard->default_grab;
   wl_signal_init(&keyboard->focus_signal);
}

EAPI void 
wl_keyboard_set_focus(struct wl_keyboard *keyboard, struct wl_resource *surface)
{
   struct wl_resource *res;
   struct wl_list *lst;
   uint32_t serial;

   lst = &keyboard->focus_resource_list;

   if ((!wl_list_empty(lst)) && (keyboard->focus != surface))
     {
        serial = wl_display_next_serial(_e_wl_comp->wl.display);

        wl_resource_for_each(res, lst)
          wl_keyboard_send_leave(res, serial, keyboard->focus);

        _move_resources(&keyboard->resource_list, lst);
     }

   if ((_find_resource_for_surface(&keyboard->resource_list, surface)) && 
       (keyboard->focus != surface))
     {       
        struct wl_client *client;

        client = wl_resource_get_client(surface);
        serial = wl_display_next_serial(_e_wl_comp->wl.display);

        _move_resources_for_client(lst, &keyboard->resource_list, client);

        wl_resource_for_each(res, lst)
          {
             wl_keyboard_send_modifiers(res, serial,
                                        keyboard->modifiers.mods_depressed,
                                        keyboard->modifiers.mods_latched,
                                        keyboard->modifiers.mods_locked,
                                        keyboard->modifiers.group);
             wl_keyboard_send_enter(res, serial, surface, &keyboard->keys);
          }

        keyboard->focus_serial = serial;
     }

   keyboard->focus = surface;
   wl_signal_emit(&keyboard->focus_signal, keyboard);
}

EAPI void 
wl_keyboard_start_grab(struct wl_keyboard *device, struct wl_keyboard_grab *grab)
{
   device->grab = grab;
   grab->keyboard = device;
}

EAPI void 
wl_keyboard_end_grab(struct wl_keyboard *keyboard)
{
   keyboard->grab = &keyboard->default_grab;
}

EAPI void 
wl_touch_init(struct wl_touch *touch)
{
   memset(touch, 0, sizeof *touch);
   wl_list_init(&touch->resource_list);
   wl_list_init(&touch->focus_resource_list);
   touch->default_grab.interface = &_e_default_touch_grab_interface;
   touch->default_grab.touch = touch;
   touch->grab = &touch->default_grab;
   wl_signal_init(&touch->focus_signal);
}

EAPI void 
wl_touch_start_grab(struct wl_touch *device, struct wl_touch_grab *grab)
{
   device->grab = grab;
   grab->touch = device;
}

EAPI void 
wl_touch_end_grab(struct wl_touch *touch)
{
   touch->grab = &touch->default_grab;
}

EAPI void 
wl_data_device_set_keyboard_focus(struct wl_seat *seat)
{
   struct wl_resource *data_device, *focus, *offer;
   struct wl_data_source *source;

   if (!seat->keyboard) return;

   focus = seat->keyboard->focus;
   if (!focus) return;

   data_device = 
     wl_resource_find_for_client(&seat->drag_resource_list, 
                                 wl_resource_get_client(focus));
   if (!data_device) return;

   source = seat->selection_data_source;
   if (source) 
     {
        offer = wl_data_source_send_offer(source, data_device);
        wl_data_device_send_selection(data_device, offer);
     }
}

EAPI int 
wl_data_device_manager_init(struct wl_display *display)
{
   if (!wl_global_create(display, &wl_data_device_manager_interface, 1, 
                         NULL, _bind_manager))
     return -1;
   return 0;
}

EAPI struct wl_resource *
wl_data_source_send_offer(struct wl_data_source *source, struct wl_resource *target)
{
   struct wl_data_offer *offer;
   char **p;

   offer = malloc(sizeof *offer);
   if (offer == NULL) return NULL;

   offer->resource = 
     wl_resource_create(wl_resource_get_client(target),
                        &wl_data_offer_interface, 1, 0);
   if (!offer->resource)
     {
        free(offer);
        return NULL;
     }

   wl_resource_set_implementation(offer->resource, &_e_data_offer_interface, 
                                  offer, _destroy_data_offer);

   offer->source = source;
   offer->source_destroy_listener.notify = _destroy_offer_data_source;
   wl_signal_add(&source->destroy_signal,
                 &offer->source_destroy_listener);

   wl_data_device_send_data_offer(target, offer->resource);

   wl_array_for_each(p, &source->mime_types)
     wl_data_offer_send_offer(offer->resource, *p);

   return offer->resource;
}

EAPI void
wl_seat_set_selection(struct wl_seat *seat, struct wl_data_source *source, uint32_t serial)
{
   struct wl_resource *data_device, *offer;
   struct wl_resource *focus = NULL;

   if (seat->selection_data_source &&
       seat->selection_serial - serial < UINT32_MAX / 2)
     return;

   if (seat->selection_data_source) 
     {
        seat->selection_data_source->cancel(seat->selection_data_source);
        wl_list_remove(&seat->selection_data_source_listener.link);
        seat->selection_data_source = NULL;
     }

   seat->selection_data_source = source;
   seat->selection_serial = serial;
   if (seat->keyboard)
     focus = seat->keyboard->focus;
   if (focus) 
     {
        data_device = 
          wl_resource_find_for_client(&seat->drag_resource_list, 
                                      wl_resource_get_client(focus));
        if (data_device && source) 
          {
             offer = wl_data_source_send_offer(seat->selection_data_source,
                                               data_device);
             wl_data_device_send_selection(data_device, offer);
          }
        else if (data_device) 
          {
             wl_data_device_send_selection(data_device, NULL);
          }
     }

   wl_signal_emit(&seat->selection_signal, seat);
   if (source) 
     {
        seat->selection_data_source_listener.notify =
          _destroy_selection_data_source;
        wl_signal_add(&source->destroy_signal,
                      &seat->selection_data_source_listener);
     }
}

EAPI unsigned int 
e_comp_wl_time_get(void)
{
   struct timeval tm;

   gettimeofday(&tm, NULL);
   return (tm.tv_sec * 1000 + tm.tv_usec / 1000);
}

EAPI void 
e_comp_wl_input_modifiers_update(unsigned int serial)
{
   struct wl_keyboard *kbd;
   struct wl_keyboard_grab *grab;
   unsigned int pressed = 0, latched = 0, locked = 0, group = 0;
   Eina_Bool changed = EINA_FALSE;

   /* check for valid keyboard */
   if (!(kbd = _e_wl_comp->input->wl.seat.keyboard)) 
     return;

   /* try to get the current keyboard's grab interface. 
    * Fallback to the default grab */
   if (!(grab = kbd->grab)) grab = &kbd->default_grab;

   pressed = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, 
                                      XKB_STATE_DEPRESSED);
   latched = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, 
                                      XKB_STATE_LATCHED);
   locked = xkb_state_serialize_mods(_e_wl_comp->input->xkb.state, 
                                     XKB_STATE_LOCKED);
   group = xkb_state_serialize_group(_e_wl_comp->input->xkb.state, 
                                     XKB_STATE_EFFECTIVE);

   if ((pressed != kbd->modifiers.mods_depressed) || 
       (latched != kbd->modifiers.mods_latched) || 
       (locked != kbd->modifiers.mods_locked) || 
       (group != kbd->modifiers.group))
     changed = EINA_TRUE;

   kbd->modifiers.mods_depressed = pressed;
   kbd->modifiers.mods_latched = latched;
   kbd->modifiers.mods_locked = locked;
   kbd->modifiers.group = group;

   /* TODO: update leds ? */

   if (changed)
     grab->interface->modifiers(grab, serial, 
                                kbd->modifiers.mods_depressed, 
                                kbd->modifiers.mods_latched, 
                                kbd->modifiers.mods_locked,
                                kbd->modifiers.group);
}

/* local functions */
static void
_seat_send_updated_caps(struct wl_seat *seat)
{
   struct wl_resource *res;
   enum wl_seat_capability caps = 0;

   if (seat->pointer)
     caps |= WL_SEAT_CAPABILITY_POINTER;
   if (seat->keyboard)
     caps |= WL_SEAT_CAPABILITY_KEYBOARD;
   if (seat->touch)
     caps |= WL_SEAT_CAPABILITY_TOUCH;

   wl_resource_for_each(res, &seat->base_resource_list)
     wl_seat_send_capabilities(res, caps);
}

static void 
_move_resources(struct wl_list *dest, struct wl_list *src)
{
   wl_list_insert_list(dest, src);
   wl_list_init(src);
}

static void 
_move_resources_for_client(struct wl_list *dest, struct wl_list *src, struct wl_client *client)
{
   struct wl_resource *res, *tmp;

   wl_resource_for_each_safe(res, tmp, src)
     {
        if (wl_resource_get_client(res) == client)
          {
             wl_list_remove(wl_resource_get_link(res));
             wl_list_insert(dest, wl_resource_get_link(res));
          }
     }
}

static struct wl_resource *
_find_resource_for_surface(struct wl_list *list, struct wl_resource *surface)
{
   if (!surface) return NULL;

   return wl_resource_find_for_client(list, wl_resource_get_client(surface));
}

static void
_default_grab_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y)
{
   struct wl_pointer *pointer = grab->pointer;

   if (pointer->button_count > 0) return;

   wl_pointer_set_focus(pointer, surface, x, y);
}

static void
_default_grab_motion(struct wl_pointer_grab *grab, uint32_t timestamp, wl_fixed_t x, wl_fixed_t y)
{
   struct wl_list *lst;
   struct wl_resource *res;

   lst = &grab->pointer->focus_resource_list;
   wl_resource_for_each(res, lst)
     wl_pointer_send_motion(res, timestamp, x, y);
}

static void
_default_grab_button(struct wl_pointer_grab *grab, uint32_t timestamp, uint32_t button, uint32_t state_w)
{
   struct wl_pointer *pointer = grab->pointer;
   struct wl_list *lst;
   struct wl_resource *res;
   enum wl_pointer_button_state state = state_w;

   lst = &pointer->focus_resource_list;
   if (!wl_list_empty(lst))
     {
        uint32_t serial;

        serial = wl_display_next_serial(_e_wl_comp->wl.display);

        wl_resource_for_each(res, lst)
          {
             switch (button)
               {
                case BTN_LEFT:
                case BTN_MIDDLE:
                case BTN_RIGHT:
                  wl_pointer_send_button(res, serial, timestamp, 
                                         button, state_w);
                  break;
                case 4:
                  if (state_w)
                    wl_pointer_send_axis(res, timestamp, 
                                         WL_POINTER_AXIS_VERTICAL_SCROLL, 
                                         -wl_fixed_from_int(1));
                  break;
                case 5:
                  if (state_w)
                    wl_pointer_send_axis(res, timestamp, 
                                         WL_POINTER_AXIS_VERTICAL_SCROLL, 
                                         wl_fixed_from_int(1));
                  break;
                case 6:
                  if (state_w)
                    wl_pointer_send_axis(res, timestamp, 
                                         WL_POINTER_AXIS_HORIZONTAL_SCROLL, 
                                         -wl_fixed_from_int(1));
                  break;
                case 7:
                  if (state_w)
                    wl_pointer_send_axis(res, timestamp, 
                                         WL_POINTER_AXIS_HORIZONTAL_SCROLL, 
                                         wl_fixed_from_int(1));
                  break;
               }
          }
     }

   if (pointer->button_count == 0 &&
       state == WL_POINTER_BUTTON_STATE_RELEASED)
     wl_pointer_set_focus(pointer, pointer->current,
                          pointer->current_x, pointer->current_y);
}

static void 
_default_grab_touch_down(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id, wl_fixed_t sx, wl_fixed_t sy)
{
   struct wl_resource *res;
   struct wl_list *lst;
   struct wl_touch *touch = grab->touch;

   lst = &touch->focus_resource_list;
   if (!wl_list_empty(lst) && (touch->focus))
     {
        uint32_t serial;

        serial = wl_display_next_serial(_e_wl_comp->wl.display);
        wl_resource_for_each(res, lst)
          wl_touch_send_down(res, serial, timestamp,
                             touch->focus, touch_id, sx, sy);
     }
}

static void 
_default_grab_touch_up(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id)
{
   struct wl_resource *res;
   struct wl_list *lst;
   struct wl_touch *touch = grab->touch;

   lst = &touch->focus_resource_list;
   if (!wl_list_empty(lst) && (touch->focus))
     {
        uint32_t serial;

        serial = wl_display_next_serial(_e_wl_comp->wl.display);
        wl_resource_for_each(res, lst)
          wl_touch_send_up(res, serial, timestamp, touch_id);
     }
}

static void 
_default_grab_touch_motion(struct wl_touch_grab *grab, uint32_t timestamp, int touch_id, wl_fixed_t sx, wl_fixed_t sy)
{
   struct wl_resource *res;
   struct wl_list *lst;
   struct wl_touch *touch = grab->touch;

   lst = &touch->focus_resource_list;
   wl_resource_for_each(res, lst)
     wl_touch_send_motion(res, timestamp, touch_id, sx, sy);
}

static void
_data_offer_accept(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial, const char *mime_type)
{
   struct wl_data_offer *offer;

   offer = wl_resource_get_user_data(resource);

   if (offer->source)
     offer->source->accept(offer->source, serial, mime_type);
}

static void
_data_offer_receive(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *mime_type, int32_t fd)
{
   struct wl_data_offer *offer;

   offer = wl_resource_get_user_data(resource);

   if (offer->source)
     offer->source->send(offer->source, mime_type, fd);
   else
     close(fd);
}

static void
_data_offer_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

static void
_destroy_data_offer(struct wl_resource *resource)
{
   struct wl_data_offer *offer;

   offer = wl_resource_get_user_data(resource);

   if (offer->source)
     wl_list_remove(&offer->source_destroy_listener.link);
   free(offer);
}

static void
_destroy_offer_data_source(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_data_offer *offer;

   offer = container_of(listener, struct wl_data_offer,
                        source_destroy_listener);
   offer->source = NULL;
}

static void
_create_data_source(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
   struct wl_data_source *source;

   source = malloc(sizeof *source);
   if (source == NULL) 
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   wl_signal_init(&source->destroy_signal);
   source->accept = _client_source_accept;
   source->send = _client_source_send;
   source->cancel = _client_source_cancel;

   wl_array_init(&source->mime_types);

   source->resource = 
     wl_resource_create(client, &wl_data_source_interface, 1, id);

   wl_resource_set_implementation(source->resource, 
                                  &_e_data_source_interface, source, 
                                  _destroy_data_source);
}

static void 
_unbind_data_device(struct wl_resource *resource)
{
   wl_list_remove(wl_resource_get_link(resource));
}

static void
_get_data_device(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource)
{
   struct wl_seat *seat;
   struct wl_resource *resource;

   seat = wl_resource_get_user_data(seat_resource);

   resource = 
     wl_resource_create(client, &wl_data_device_interface, 1, id);
   if (!resource)
     {
        wl_resource_post_no_memory(manager_resource);
        return;
     }

   wl_list_insert(&seat->drag_resource_list, wl_resource_get_link(resource));

   wl_resource_set_implementation(resource, &_e_data_device_interface, seat, 
                                  _unbind_data_device);

}

static void
_current_surface_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_pointer *pointer =
     container_of(listener, struct wl_pointer, current_listener);
   pointer->current = NULL;
}

static void
_bind_manager(struct wl_client *client, void *data EINA_UNUSED, uint32_t version EINA_UNUSED, uint32_t id)
{
   struct wl_resource *res;

   res = wl_resource_create(client, &wl_data_device_manager_interface, 1, id);
   if (!res)
     {
        wl_client_post_no_memory(client);
        return;
     }

   wl_resource_set_implementation(res, &_e_manager_interface, NULL, NULL);
}

static void
_default_grab_key(struct wl_keyboard_grab *grab, uint32_t timestamp, uint32_t key, uint32_t state)
{
   struct wl_keyboard *keyboard = grab->keyboard;
   struct wl_resource *res;
   struct wl_list *lst;

   lst = &keyboard->focus_resource_list;
   if (!wl_list_empty(lst))
     {
        uint32_t serial;

        serial = wl_display_next_serial(_e_wl_comp->wl.display);
        wl_resource_for_each(res, lst)
          wl_keyboard_send_key(res, serial, timestamp, key, state);
     }
}

static void
_default_grab_modifiers(struct wl_keyboard_grab *grab, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group)
{
   struct wl_keyboard *keyboard = grab->keyboard;
   struct wl_pointer *pointer = keyboard->seat->pointer;
   struct wl_resource *res;
   struct wl_list *lst;

   lst = &keyboard->focus_resource_list;
   wl_resource_for_each(res, lst)
     wl_keyboard_send_modifiers(res, serial, mods_depressed, mods_latched,
                                mods_locked, group);

   if (pointer && pointer->focus && pointer->focus != keyboard->focus) 
     {
        struct wl_client *client;

        lst = &keyboard->resource_list;
        client = wl_resource_get_client(pointer->focus);
        wl_resource_for_each(res, lst)
          {
             if (wl_resource_get_client(res) == client)
               wl_keyboard_send_modifiers(res, serial, mods_depressed, 
                                          mods_latched, mods_locked, group);
          }
     }
}

static void
_data_device_start_drag(struct wl_client *client, struct wl_resource *resource, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, uint32_t serial EINA_UNUSED)
{
   struct wl_seat *seat;

   seat = wl_resource_get_user_data(resource);

   if ((seat->pointer->button_count == 0) || 
       (seat->pointer->grab_serial != serial) || 
       (seat->pointer->focus != wl_resource_get_user_data(origin_resource)))
     return;

   seat->drag_grab.interface = &_e_drag_grab_interface;
   seat->drag_client = client;
   seat->drag_data_source = NULL;

   if (source_resource) 
     {
        struct wl_data_source *source;

        source = wl_resource_get_user_data(source_resource);
        seat->drag_data_source = source;
        seat->drag_data_source_listener.notify =
          _destroy_data_device_source;
        wl_signal_add(&source->destroy_signal,
                      &seat->drag_data_source_listener);
       }

   if (icon_resource) 
     {
        E_Wayland_Surface *icon;

        icon = wl_resource_get_user_data(icon_resource);

        seat->drag_surface = icon->wl.surface;
        seat->drag_icon_listener.notify = _destroy_data_device_icon;
        wl_signal_add(&icon->wl.destroy_signal,
                      &seat->drag_icon_listener);
        /* wl_signal_emit(&seat->drag_icon_signal, icon_resource); */
     }

   wl_pointer_set_focus(seat->pointer, NULL,
                        wl_fixed_from_int(0), wl_fixed_from_int(0));
   wl_pointer_start_grab(seat->pointer, &seat->drag_grab);
}

static void
_data_device_set_selection(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *source_resource, uint32_t serial)
{
   struct wl_data_source *source;
   struct wl_seat *seat;

   if (!(seat = wl_resource_get_user_data(resource))) return;
   source = wl_resource_get_user_data(source_resource);

   wl_seat_set_selection(seat, source, serial);
}

static void
_destroy_data_device_icon(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_seat *seat = 
     container_of(listener, struct wl_seat, drag_icon_listener);

   seat->drag_surface = NULL;
}

static void
_destroy_selection_data_source(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_seat *seat = 
     container_of(listener, struct wl_seat, selection_data_source_listener);
   struct wl_resource *data_device;
   struct wl_resource *focus = NULL;

   seat->selection_data_source = NULL;

   if (seat->keyboard)
     focus = seat->keyboard->focus;

   if (focus)
     {
        data_device = 
          wl_resource_find_for_client(&seat->drag_resource_list,
                                    wl_resource_get_client(focus));
        if (data_device)
          wl_data_device_send_selection(data_device, NULL);
     }

   wl_signal_emit(&seat->selection_signal, seat);
}

static void
_destroy_data_device_source(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_seat *seat = 
     container_of(listener, struct wl_seat, drag_data_source_listener);
   _data_device_end_drag_grab(seat);
}

static void
_data_source_offer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *type)
{
   struct wl_data_source *source;
   char **p;

   source = wl_resource_get_user_data(resource);
   p = wl_array_add(&source->mime_types, sizeof *p);
   if (p) *p = strdup(type);

   if (!p || !*p) wl_resource_post_no_memory(resource);
}

static void
_data_source_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

static void
_destroy_data_source(struct wl_resource *resource)
{
   struct wl_data_source *source;
   char **p;

   source = wl_resource_get_user_data(resource);

   wl_signal_emit(&source->destroy_signal, source);

   wl_array_for_each(p, &source->mime_types)
     free(*p);

   wl_array_release(&source->mime_types);

   source->resource = NULL;
}

static void
_data_device_end_drag_grab(struct wl_seat *seat)
{
   if (seat->drag_surface) 
     {
        seat->drag_surface = NULL;
        wl_signal_emit(&seat->drag_icon_signal, NULL);
        wl_list_remove(&seat->drag_icon_listener.link);
     }

   _drag_grab_focus(&seat->drag_grab, NULL,
                    wl_fixed_from_int(0), wl_fixed_from_int(0));
   wl_pointer_end_grab(seat->pointer);
   seat->drag_data_source = NULL;
   seat->drag_client = NULL;
}

static void
_drag_grab_button(struct wl_pointer_grab *grab, uint32_t timestamp EINA_UNUSED, uint32_t button, uint32_t state_w)
{
   struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);
   enum wl_pointer_button_state state = state_w;

   if (seat->drag_focus_resource &&
       seat->pointer->grab_button == button &&
       state == WL_POINTER_BUTTON_STATE_RELEASED)
     wl_data_device_send_drop(seat->drag_focus_resource);

   if (seat->pointer->button_count == 0 &&
       state == WL_POINTER_BUTTON_STATE_RELEASED) 
     {
        if (seat->drag_data_source)
          wl_list_remove(&seat->drag_data_source_listener.link);

        _data_device_end_drag_grab(seat);
     }
}

static void
_drag_grab_motion(struct wl_pointer_grab *grab, uint32_t timestamp, wl_fixed_t x, wl_fixed_t y)
{
   struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);

   if (seat->drag_focus_resource)
     wl_data_device_send_motion(seat->drag_focus_resource,
                                timestamp, x, y);
}

static void
_destroy_drag_focus(struct wl_listener *listener, void *data EINA_UNUSED)
{
   struct wl_seat *seat =
     container_of(listener, struct wl_seat, drag_focus_listener);

   seat->drag_focus_resource = NULL;
}

static void
_drag_grab_focus(struct wl_pointer_grab *grab, struct wl_resource *surface, wl_fixed_t x, wl_fixed_t y)
{
   struct wl_seat *seat = container_of(grab, struct wl_seat, drag_grab);
   struct wl_resource *resource, *offer = NULL;
   struct wl_display *display;
   uint32_t serial;
   E_Wayland_Surface *ews;

   if (seat->drag_focus_resource) 
     {
        wl_data_device_send_leave(seat->drag_focus_resource);
        wl_list_remove(&seat->drag_focus_listener.link);
        seat->drag_focus_resource = NULL;
        seat->drag_focus = NULL;
     }

   if (!surface) return;

   if (!seat->drag_data_source &&
       wl_resource_get_client(surface) != seat->drag_client)
     return;

   resource = 
     wl_resource_find_for_client(&seat->drag_resource_list, 
                                 wl_resource_get_client(surface));
   if (!resource) return;

   display = wl_client_get_display(wl_resource_get_client(resource));
   serial = wl_display_next_serial(display);

   if (seat->drag_data_source)
     offer = wl_data_source_send_offer(seat->drag_data_source,
                                       resource);

   wl_data_device_send_enter(resource, serial, surface, x, y, offer);

   ews = wl_resource_get_user_data(surface);

   seat->drag_focus = surface;
   seat->drag_focus_listener.notify = _destroy_drag_focus;
   wl_signal_add(&ews->wl.destroy_signal, &seat->drag_focus_listener);
   seat->drag_focus_resource = resource;
   grab->focus = surface;
}

static void
_client_source_accept(struct wl_data_source *source, uint32_t timestamp EINA_UNUSED, const char *mime_type)
{
   wl_data_source_send_target(source->resource, mime_type);
}

static void
_client_source_send(struct wl_data_source *source, const char *mime_type, int32_t fd)
{
   wl_data_source_send_send(source->resource, mime_type, fd);
   close(fd);
}

static void
_client_source_cancel(struct wl_data_source *source)
{
   wl_data_source_send_cancelled(source->resource);
}

static void 
_e_comp_wl_cb_bind(struct wl_client *client, void *data, unsigned int version, unsigned int id)
{
   E_Wayland_Compositor *comp;
   struct wl_resource *res;

   if (!(comp = data)) return;

   res = 
     wl_resource_create(client, &wl_compositor_interface, MIN(version, 3), id);
   if (res)
     wl_resource_set_implementation(res, &_e_compositor_interface, comp, NULL);
}

static Eina_Bool 
_e_comp_wl_cb_read(void *data EINA_UNUSED, Ecore_Fd_Handler *hdl EINA_UNUSED)
{
   /* flush any events before we sleep */
   wl_display_flush_clients(_e_wl_comp->wl.display);
   wl_event_loop_dispatch(_e_wl_comp->wl.loop, 0);

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool 
_e_comp_wl_cb_idle(void *data EINA_UNUSED)
{
   if ((_e_wl_comp) && (_e_wl_comp->wl.display))
     {
        /* flush any clients before we idle */
        wl_display_flush_clients(_e_wl_comp->wl.display);
     }

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool 
_e_comp_wl_cb_module_idle(void *data EINA_UNUSED)
{
   E_Module *mod = NULL;

   /* if we are still in the process of loading modules, then we will wait */
   if (e_module_loading_get()) return ECORE_CALLBACK_RENEW;

   /* try to find the shell module, and create it if not found
    * 
    * TODO: we should have a config variable somewhere to store which 
    * shell we want to load (tablet, mobile, etc) */
   if (!(mod = e_module_find("wl_desktop_shell")))
     mod = e_module_new("wl_desktop_shell");

   /* if we have the module now, load it */
   if (mod) 
     {
        e_module_enable(mod);
        _module_idler = NULL;

        /* flush any pending events
         * 
         * NB: This advertises out any globals so it needs to be deferred 
         * until after the shell has been loaded */
        wl_event_loop_dispatch(_e_wl_comp->wl.loop, 0);

        return ECORE_CALLBACK_CANCEL;
     }

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool 
_e_comp_wl_cb_keymap_changed(void *data EINA_UNUSED, int type EINA_UNUSED, void *event EINA_UNUSED)
{
   struct xkb_keymap *keymap;

   /* try to fetch the keymap */
   if (!(keymap = _e_comp_wl_input_keymap_get())) 
     return ECORE_CALLBACK_PASS_ON;

   /* destroy keyboard */
   if (_e_wl_comp->input->xkb.info)
     {
        /* if we have a keymap, unreference it */
        if (_e_wl_comp->input->xkb.info->keymap)
          xkb_map_unref(_e_wl_comp->input->xkb.info->keymap);

        /* if we have a keymap mmap'd area, unmap it */
        if (_e_wl_comp->input->xkb.info->area)
          munmap(_e_wl_comp->input->xkb.info->area, 
                 _e_wl_comp->input->xkb.info->size);

        /* if we created an fd for keyboard input, close it */
        if (_e_wl_comp->input->xkb.info->fd) 
          close(_e_wl_comp->input->xkb.info->fd);

        /* free the allocated keyboard info structure */
        E_FREE(_e_wl_comp->input->xkb.info);
     }

   /* unreference the xkb state we created */
   if (_e_wl_comp->input->xkb.state) 
     xkb_state_unref(_e_wl_comp->input->xkb.state);

   /* unreference the xkb context we created */
   if (_e_wl_comp->xkb.context)
     xkb_context_unref(_e_wl_comp->xkb.context);

   /* create the xkb context */
   _e_wl_comp->xkb.context = xkb_context_new(0);

   /* try to fetch the keymap */
//   if ((keymap = _e_comp_wl_input_keymap_get()))
     {
        /* try to create new keyboard info */
        _e_wl_comp->input->xkb.info = 
          _e_comp_wl_input_keyboard_info_get(keymap);

        /* create new xkb state */
        _e_wl_comp->input->xkb.state = xkb_state_new(keymap);

        /* unreference the keymap */
        xkb_map_unref(keymap);
     }

   /* check for valid keyboard */
   if (!_e_wl_comp->input->wl.keyboard_resource) 
     return ECORE_CALLBACK_PASS_ON;

   /* send the current keymap to the keyboard object */
   wl_keyboard_send_keymap(_e_wl_comp->input->wl.keyboard_resource, 
                           WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, 
                           _e_wl_comp->input->xkb.info->fd, 
                           _e_wl_comp->input->xkb.info->size);

   return ECORE_CALLBACK_PASS_ON;
}

/* compositor interface functions */
static void 
_e_comp_wl_cb_surface_create(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
   E_Wayland_Surface *ews = NULL;

   /* try to allocate space for a new surface */
   if (!(ews = E_NEW(E_Wayland_Surface, 1)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }
   ews->id = id;

   /* initialize the destroy signal */
   wl_signal_init(&ews->wl.destroy_signal);

   /* initialize the link */
   wl_list_init(&ews->wl.link);

   /* initialize the lists of frames */
   wl_list_init(&ews->wl.frames);
   wl_list_init(&ews->pending.frames);

   ews->wl.surface = NULL;

   /* set destroy function for pending buffers */
   ews->pending.buffer_destroy.notify = 
     _e_comp_wl_surface_cb_pending_buffer_destroy;

   /* initialize regions */
   pixman_region32_init(&ews->region.opaque);
   pixman_region32_init(&ews->region.damage);
   pixman_region32_init(&ews->region.clip);
   pixman_region32_init_rect(&ews->region.input, INT32_MIN, INT32_MIN, 
                             UINT32_MAX, UINT32_MAX);

   /* initialize pending regions */
   pixman_region32_init(&ews->pending.opaque);
   pixman_region32_init(&ews->pending.damage);
   pixman_region32_init_rect(&ews->pending.input, INT32_MIN, INT32_MIN, 
                             UINT32_MAX, UINT32_MAX);

   ews->wl.surface = 
     wl_resource_create(client, &wl_surface_interface, 
                        wl_resource_get_version(resource), id);
   wl_resource_set_implementation(ews->wl.surface, &_e_surface_interface, 
                                  ews, _e_comp_wl_cb_surface_destroy);

   /* add this surface to the list of surfaces */
   _e_wl_comp->surfaces = eina_inlist_append(_e_wl_comp->surfaces, EINA_INLIST_GET(ews));
}

static void 
_e_comp_wl_cb_surface_destroy(struct wl_resource *resource)
{
   E_Wayland_Surface *ews = NULL;
   E_Wayland_Surface_Frame_Callback *cb = NULL, *ncb = NULL;
   struct wl_pointer *pointer;

   /* try to get the surface from this resource */
   if (!(ews = wl_resource_get_user_data(resource)))
     return;

   pointer = &_e_wl_comp->input->wl.pointer;
   if (pointer->focus == resource)
     {
        wl_pointer_set_focus(pointer, NULL,
                             wl_fixed_from_int(0), wl_fixed_from_int(0));

        _e_wl_comp->input->pointer.surface = NULL;
     }

   /* if this surface is mapped, unmap it */
   if (ews->mapped)
     {
        if (ews->unmap) ews->unmap(ews);
     }

   /* loop any pending surface frame callbacks and destroy them */
   wl_list_for_each_safe(cb, ncb, &ews->pending.frames, wl.link)
     wl_resource_destroy(cb->wl.resource);

   /* clear any pending regions */
   pixman_region32_fini(&ews->pending.damage);
   pixman_region32_fini(&ews->pending.opaque);
   pixman_region32_fini(&ews->pending.input);

   /* remove the pending buffer from the list */
   if (ews->pending.buffer)
     wl_list_remove(&ews->pending.buffer_destroy.link);

   /* dereference any existing buffers */
   _e_comp_wl_surface_buffer_reference(&ews->buffer_reference, NULL);

   /* clear any active regions */
   pixman_region32_fini(&ews->region.damage);
   pixman_region32_fini(&ews->region.opaque);
   pixman_region32_fini(&ews->region.input);
   pixman_region32_fini(&ews->region.clip);

   /* loop any active surface frame callbacks and destroy them */
   wl_list_for_each_safe(cb, ncb, &ews->wl.frames, wl.link)
     wl_resource_destroy(cb->wl.resource);

   /* remove this surface from the compositor's list of surfaces */
   _e_wl_comp->surfaces = eina_inlist_remove(_e_wl_comp->surfaces, EINA_INLIST_GET(ews));

   /* free the allocated surface structure */
   E_FREE(ews);
}

static void 
_e_comp_wl_cb_region_create(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
   E_Wayland_Region *ewr = NULL;

   /* try to allocate space for a new region */
   if (!(ewr = E_NEW_RAW(E_Wayland_Region, 1)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   pixman_region32_init(&ewr->region);

   ewr->wl.resource = 
     wl_resource_create(client, &wl_region_interface, 
                        wl_resource_get_version(resource), id);
   wl_resource_set_implementation(ewr->wl.resource, &_e_region_interface, ewr, 
                                  _e_comp_wl_cb_region_destroy);
}

static void 
_e_comp_wl_cb_region_destroy(struct wl_resource *resource)
{
   E_Wayland_Region *ewr = NULL;

   /* try to get the region from this resource */
   if (!(ewr = wl_resource_get_user_data(resource)))
     return;

   /* tell pixman we are finished with this region */
   pixman_region32_fini(&ewr->region);

   /* free the allocated region structure */
   E_FREE(ewr);
}

/* input functions */
static Eina_Bool 
_e_comp_wl_input_init(void)
{
   struct xkb_keymap *keymap;

   /* try to allocate space for a new compositor */
   if (!(_e_wl_comp->input = E_NEW(E_Wayland_Input, 1)))
     return EINA_FALSE;

   /* initialize the seat */
   wl_seat_init(&_e_wl_comp->input->wl.seat);

   /* try to add this input to the diplay's list of globals */
   if (!wl_global_create(_e_wl_comp->wl.display, &wl_seat_interface, 2, 
                         _e_wl_comp->input, _e_comp_wl_input_cb_bind))
     {
        ERR("Could not add Input to Wayland Display Globals: %m");
        goto err;
     }

   _e_wl_comp->input->pointer.surface = NULL;
   _e_wl_comp->input->pointer.surface_destroy.notify = 
     _e_comp_wl_pointer_cb_destroy;
   _e_wl_comp->input->pointer.hot.x = 16;
   _e_wl_comp->input->pointer.hot.y = 16;

   /* initialize wayland pointer */
   wl_pointer_init(&_e_wl_comp->input->wl.pointer);

   /* tell the seat about this pointer */
   wl_seat_set_pointer(&_e_wl_comp->input->wl.seat, 
                       &_e_wl_comp->input->wl.pointer);

   /* set flag to indicate we have a pointer */
   _e_wl_comp->input->has_pointer = EINA_TRUE;

   /* create the xkb context */
   _e_wl_comp->xkb.context = xkb_context_new(0);

   /* try to fetch the keymap */
   if ((keymap = _e_comp_wl_input_keymap_get()))
     {
        /* try to create new keyboard info */
        _e_wl_comp->input->xkb.info = 
          _e_comp_wl_input_keyboard_info_get(keymap);

        /* create new xkb state */
        _e_wl_comp->input->xkb.state = xkb_state_new(keymap);

        /* unreference the keymap */
        xkb_map_unref(keymap);
     }

   /* initialize the keyboard */
   wl_keyboard_init(&_e_wl_comp->input->wl.keyboard);

   /* tell the seat about this keyboard */
   wl_seat_set_keyboard(&_e_wl_comp->input->wl.seat, 
                        &_e_wl_comp->input->wl.keyboard);

   /* set flag to indicate we have a keyboard */
   _e_wl_comp->input->has_keyboard = EINA_TRUE;

   wl_list_init(&_e_wl_comp->input->wl.link);

   /* append this input to the list */
   _e_wl_comp->seats = eina_list_append(_e_wl_comp->seats, _e_wl_comp->input);

   /* emit a signal saying that input has been initialized */
   wl_signal_emit(&_e_wl_comp->signals.seat, _e_wl_comp->input);

   /* return success */
   return EINA_TRUE;

err:
   /* release the wl_seat */
   wl_seat_release(&_e_wl_comp->input->wl.seat);

   /* free the allocated input structure */
   E_FREE(_e_wl_comp->input);

   /* return failure */
   return EINA_FALSE;
}

static void 
_e_comp_wl_input_shutdown(void)
{
   /* safety check */
   if (!_e_wl_comp->input) return;

   /* destroy keyboard */
   if (_e_wl_comp->input->xkb.info)
     {
        /* if we have a keymap, unreference it */
        if (_e_wl_comp->input->xkb.info->keymap)
          xkb_map_unref(_e_wl_comp->input->xkb.info->keymap);

        /* if we have a keymap mmap'd area, unmap it */
        if (_e_wl_comp->input->xkb.info->area)
          munmap(_e_wl_comp->input->xkb.info->area, 
                 _e_wl_comp->input->xkb.info->size);

        /* if we created an fd for keyboard input, close it */
        if (_e_wl_comp->input->xkb.info->fd) 
          close(_e_wl_comp->input->xkb.info->fd);

        /* free the allocated keyboard info structure */
        E_FREE(_e_wl_comp->input->xkb.info);
     }

   /* unreference the xkb state we created */
   if (_e_wl_comp->input->xkb.state) 
     xkb_state_unref(_e_wl_comp->input->xkb.state);

   /* unreference the xkb context we created */
   if (_e_wl_comp->xkb.context)
     xkb_context_unref(_e_wl_comp->xkb.context);

   /* TODO: destroy pointer surface
    * 
    * NB: Currently, we do not create one */

   wl_list_remove(&_e_wl_comp->input->wl.link);

   /* release the seat */
   wl_seat_release(&_e_wl_comp->input->wl.seat);

   /* free the allocated input structure */
   E_FREE(_e_wl_comp->input);
}

static void 
_e_comp_wl_input_cb_bind(struct wl_client *client, void *data, unsigned int version, unsigned int id)
{
   struct wl_seat *seat = NULL;
   struct wl_resource *resource = NULL;
   enum wl_seat_capability caps = 0;

   /* try to cast data to our seat */
   if (!(seat = data)) return;

   /* add the seat object to the client */
   resource = 
     wl_resource_create(client, &wl_seat_interface, MIN(version, 2), id);

   wl_list_insert(&seat->base_resource_list, wl_resource_get_link(resource));

   wl_resource_set_implementation(resource, &_e_input_interface, data, 
                                  _e_comp_wl_input_cb_unbind);

   /* set capabilities based on seat */
   if (seat->pointer) caps |= WL_SEAT_CAPABILITY_POINTER;
   if (seat->keyboard) caps |= WL_SEAT_CAPABILITY_KEYBOARD;
   if (seat->touch) caps |= WL_SEAT_CAPABILITY_TOUCH;

   /* inform the resource about the seat capabilities */
   wl_seat_send_capabilities(resource, caps);
}

static void 
_e_comp_wl_input_cb_unbind(struct wl_resource *resource)
{
   /* remove the link */
   wl_list_remove(wl_resource_get_link(resource));
}

static struct xkb_keymap *
_e_comp_wl_input_keymap_get(void)
{
   E_Config_XKB_Layout *kbd_layout;
   struct xkb_keymap *keymap;
   struct xkb_rule_names names;

   memset(&names, 0, sizeof(names));

   if ((kbd_layout = e_xkb_layout_get()))
     {
        names.model = strdup(kbd_layout->model);
        names.layout = strdup(kbd_layout->name);
     }

   /* if we are running under X11, try to get the xkb rule names atom */
   if (getenv("DISPLAY"))
     {
        Ecore_X_Atom rules = 0;
        Ecore_X_Window root = 0;
        int len = 0;
        unsigned char *data;

        /* TODO: FIXME: NB:
         * 
         * The below commented out code is for using the "already" configured 
         * E xkb settings in the wayland clients. The only Major problem with 
         * that is: That the E_Config_XKB_Layout does not define a 
         * 'RULES' which we need ....
         * 
         */

        root = ecore_x_window_root_first_get();
        rules = ecore_x_atom_get("_XKB_RULES_NAMES");
        ecore_x_window_prop_property_get(root, rules, ECORE_X_ATOM_STRING, 
                                         1024, &data, &len);

        if ((data) && (len > 0))
          {
             names.rules = strdup((const char *)data);
             data += strlen((const char *)data) + 1;
             if (!names.model)
               names.model = strdup((const char *)data);
             data += strlen((const char *)data) + 1;
             if (!names.layout)
               names.layout = strdup((const char *)data);
          }
     }

   printf("Keymap\n");
   printf("\tRules: %s\n", names.rules);
   printf("\tModel: %s\n", names.model);
   printf("\tLayout: %s\n", names.layout);

   keymap = xkb_map_new_from_names(_e_wl_comp->xkb.context, &names, 0);

   free((char *)names.rules);
   free((char *)names.model);
   free((char *)names.layout);

   return keymap;
}

static int 
_e_comp_wl_input_keymap_fd_get(off_t size)
{
   char tmp[PATH_MAX];
   const char *path;
   int fd = 0;
   long flags;

   if (!(path = getenv("XDG_RUNTIME_DIR")))
     return -1;

   strcpy(tmp, path);
   strcat(tmp, "/e-wl-keymap-XXXXXX");

   if ((fd = mkstemp(tmp)) < 0)
     return -1;

   /* TODO: really should error check the returns here */

   flags = fcntl(fd, F_GETFD);
   fcntl(fd, F_SETFD, flags | FD_CLOEXEC);

   if (ftruncate(fd, size) < 0)
     {
        close(fd);
        return -1;
     }

   unlink(tmp);
   return fd;
}

static E_Wayland_Keyboard_Info *
_e_comp_wl_input_keyboard_info_get(struct xkb_keymap *keymap)
{
   E_Wayland_Keyboard_Info *info = NULL;
   char *tmp;

   /* try to allocate space for the keyboard info structure */
   if (!(info = E_NEW(E_Wayland_Keyboard_Info, 1)))
     return NULL;

   if (!(info->keymap = xkb_map_ref(keymap)))
     {
        E_FREE(info);
        return NULL;
     }

   /* init modifiers */
   info->mod_shift = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_SHIFT);
   info->mod_caps = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CAPS);
   info->mod_ctrl = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_CTRL);
   info->mod_alt = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_ALT);
   info->mod_super = xkb_map_mod_get_index(keymap, XKB_MOD_NAME_LOGO);

   /* try to get a string of this keymap */
   if (!(tmp = xkb_map_get_as_string(keymap)))
     {
        printf("Could not get keymap as string\n");
        E_FREE(info);
        return NULL;
     }

   info->size = strlen(tmp) + 1;

   /* try to create an fd we can listen on for input */
   info->fd = _e_comp_wl_input_keymap_fd_get(info->size);
   if (info->fd < 0)
     {
        printf("Could not create keymap fd\n");
        E_FREE(info);
        return NULL;
     }

   info->area = 
     mmap(NULL, info->size, PROT_READ | PROT_WRITE, MAP_SHARED, info->fd, 0);

   /* TODO: error check mmap */

   if ((info->area) && (tmp))
     {
        strcpy(info->area, tmp);
        free(tmp);
     }

   return info;
}

/* input interface functions */
static void 
_e_comp_wl_input_cb_pointer_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
   E_Wayland_Input *input = NULL;
   struct wl_resource *ptr = NULL;

   /* try to cast the resource data to our input structure */
   if (!(input = wl_resource_get_user_data(resource)))
     return;

   /* check that input has a pointer */
   if (!input->has_pointer) return;

   /* add a pointer object to the client */
   ptr = wl_resource_create(client, &wl_pointer_interface, 
                            wl_resource_get_version(resource), id);
   wl_list_insert(&input->wl.seat.pointer->resource_list, 
                  wl_resource_get_link(ptr));
   wl_resource_set_implementation(ptr, &_e_pointer_interface, 
                                  input, _e_comp_wl_input_cb_unbind);

   /* if the pointer has a focused surface, set it */
   if ((input->wl.seat.pointer->focus) && 
       (wl_resource_get_client(input->wl.seat.pointer->focus) == client))
     {
        /* tell pointer which surface is focused */
        wl_list_remove(wl_resource_get_link(ptr));
        wl_list_insert(&input->wl.seat.pointer->focus_resource_list, 
                       wl_resource_get_link(ptr));

        wl_pointer_send_enter(ptr, input->wl.seat.pointer->focus_serial, 
                              input->wl.seat.pointer->focus, 
                              input->wl.seat.pointer->x, 
                              input->wl.seat.pointer->y);
     }
}

static void 
_e_comp_wl_input_cb_keyboard_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
   E_Wayland_Input *input = NULL;
   struct wl_resource *kbd = NULL;

   /* try to cast the resource data to our input structure */
   if (!(input = wl_resource_get_user_data(resource)))
     return;

   /* check that input has a keyboard */
   if (!input->has_keyboard) return;

   /* add a keyboard object to the client */
   kbd = wl_resource_create(client, &wl_keyboard_interface, 
                            wl_resource_get_version(resource), id);
   wl_list_insert(&input->wl.seat.keyboard->resource_list, 
                  wl_resource_get_link(kbd));
   wl_resource_set_implementation(kbd, &_e_keyboard_interface, input, 
                                  _e_comp_wl_input_cb_unbind);

   /* send the current keymap to the keyboard object */
   wl_keyboard_send_keymap(kbd, WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1, 
                           input->xkb.info->fd, input->xkb.info->size);

   if (((input->wl.seat.keyboard) && (input->wl.seat.keyboard->focus) && 
        (wl_resource_get_client(input->wl.seat.keyboard->focus) == client)) || 
       ((input->wl.seat.pointer) && (input->wl.seat.pointer->focus) && 
           (wl_resource_get_client(input->wl.seat.pointer->focus) == client)))
     {
        wl_keyboard_send_modifiers(kbd, input->wl.seat.keyboard->focus_serial,
                                   input->wl.seat.keyboard->modifiers.mods_depressed,
                                   input->wl.seat.keyboard->modifiers.mods_latched,
                                   input->wl.seat.keyboard->modifiers.mods_locked,
                                   input->wl.seat.keyboard->modifiers.group);
     }

   /* test if keyboard has a focused client */
   if ((input->wl.seat.keyboard->focus) && 
       (wl_resource_get_client(input->wl.seat.keyboard->focus) == client))
     {
        wl_list_remove(wl_resource_get_link(kbd));
        wl_list_insert(&input->wl.seat.keyboard->focus_resource_list, 
                       wl_resource_get_link(kbd));
        wl_keyboard_send_enter(kbd, input->wl.seat.keyboard->focus_serial, 
                               input->wl.seat.keyboard->focus, 
                               &input->wl.seat.keyboard->keys);

        if (input->wl.seat.keyboard->focus_resource_list.prev == 
            wl_resource_get_link(kbd))
          {
             /* update the keyboard focus in the data device */
             wl_data_device_set_keyboard_focus(&input->wl.seat);
          }
     }

   input->wl.keyboard_resource = kbd;
}

static void 
_e_comp_wl_input_cb_touch_get(struct wl_client *client, struct wl_resource *resource, unsigned int id)
{
   E_Wayland_Input *input = NULL;
   struct wl_resource *tch = NULL;

   /* try to cast the resource data to our input structure */
   if (!(input = wl_resource_get_user_data(resource)))
     return;

   /* check that input has a touch */
   if (!input->has_touch) return;

   /* add a touch object to the client */
   tch = wl_resource_create(client, &wl_touch_interface, 
                            wl_resource_get_version(resource), id);
   if ((input->wl.seat.touch->focus) && 
       (wl_resource_get_client(input->wl.seat.touch->focus) == client))
     {
        wl_list_insert(&input->wl.seat.touch->resource_list, 
                       wl_resource_get_link(tch));
     }
   else
     wl_list_insert(&input->wl.seat.touch->focus_resource_list, 
                    wl_resource_get_link(tch));

   wl_resource_set_implementation(tch, &_e_touch_interface, input, 
                                  _e_comp_wl_input_cb_unbind);
}

/* pointer functions */
static void 
_e_comp_wl_pointer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Wayland_Input *input = NULL;

   /* try to get the input from this listener */
   if (!(input = container_of(listener, E_Wayland_Input, pointer.surface_destroy)))
     return;

   input->pointer.surface = NULL;
}

static void 
_e_comp_wl_pointer_configure(E_Wayland_Surface *ews, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h)
{
   E_Wayland_Input *input = NULL;
   E_Wayland_Surface *focus = NULL;

   /* safety check */
   if (!ews) return;

   /* see if this surface has 'input' */
   if (!(input = ews->input)) return;

   input->pointer.hot.x -= x;
   input->pointer.hot.y -= y;

   /* configure the surface geometry */
   ews->geometry.x = x;
   ews->geometry.h = h;
   ews->geometry.w = w;
   ews->geometry.h = h;
   ews->geometry.changed = EINA_TRUE;

   /* tell pixman we are finished with this region */
   pixman_region32_fini(&ews->pending.input);

   /* reinitalize the pending input region */
   pixman_region32_init(&ews->pending.input);

   /* do we have a focused surface ? */
   if (!input->wl.seat.pointer->focus) return;

   if ((focus = wl_resource_get_user_data(input->wl.seat.pointer->focus)))
     {
        /* NB: Ideally, I wanted to use the e_pointer methods here so that 
         * the cursor would match the E theme, however Wayland currently 
         * provides NO Method to get the cursor name :( so we are stuck 
         * using the pixels from their cursor surface */

        /* is it mapped ? */
        if ((focus->mapped) && (focus->ee))
          {
             Ecore_Window win;

             /* try to get the ecore_window */
             if ((win = ecore_evas_window_get(focus->ee)))
               {
                  E_Wayland_Buffer_Reference *ref;
                  struct wl_shm_buffer *shm_buffer;
                  void *pixels;

                  ref = &ews->buffer_reference;

                  shm_buffer = wl_shm_buffer_get(ref->buffer->wl.resource);

                  /* grab the pixels from the cursor surface */
                  if ((pixels = wl_shm_buffer_get_data(shm_buffer)))
                    {
                       Ecore_X_Cursor cur;

                       /* create the new X cursor with this image */
                       cur = ecore_x_cursor_new(win, pixels, w, h,
                                                input->pointer.hot.x, 
                                                input->pointer.hot.y);

                       /* set the cursor on this window */
                       ecore_x_window_cursor_set(win, cur);

                       /* free the cursor */
                       ecore_x_cursor_free(cur);
                    }
                  else
                    ecore_x_window_cursor_set(win, 0);
               }
          }
     }
}

static void 
_e_comp_wl_pointer_unmap(E_Wayland_Surface *ews)
{
   E_Wayland_Input *input = NULL;

   /* safety check */
   if (!ews) return;

   if (!(input = ews->input)) return;

   wl_list_remove(&input->pointer.surface_destroy.link);

   if (input->pointer.surface)
     input->pointer.surface->configure = NULL;

   input->pointer.surface = NULL;
}

/* pointer interface functions */
static void 
_e_comp_wl_pointer_cb_cursor_set(struct wl_client *client, struct wl_resource *resource, unsigned int serial, struct wl_resource *surface_resource, int x, int y)
{
   E_Wayland_Input *input = NULL;
   E_Wayland_Surface *ews = NULL;

   /* try to cast the resource data to our input structure */
   if (!(input = wl_resource_get_user_data(resource)))
     return;

   /* if we were passed in a surface, try to cast it to our structure */
   if (surface_resource) ews = wl_resource_get_user_data(surface_resource);

   /* if this input has no pointer, get out */
   if (!input->has_pointer) return;

   /* if the input has no current focus, get out */
   if (!input->wl.seat.pointer->focus) 
     {
        /* if we have an existing pointer surface, unmap it */
        if (input->pointer.surface) 
          {
             /* call the unmap function */
             if (input->pointer.surface->unmap)
               input->pointer.surface->unmap(input->pointer.surface);
          }

        input->pointer.surface = NULL;

        return;
     }

   if (wl_resource_get_client(input->wl.seat.pointer->focus) != client) return;
   if ((input->wl.seat.pointer->focus_serial - serial) > (UINT32_MAX / 2))
     return;

   /* is the passed in surface the same as our pointer surface ? */
   if ((ews) && (ews != input->pointer.surface))
     {
        if (ews->configure)
          {
             wl_resource_post_error(ews->wl.surface, 
                                    WL_DISPLAY_ERROR_INVALID_OBJECT, 
                                    "Surface already configured");
             return;
          }
     }

   /* if we have an existing pointer surface, unmap it */
   if (input->pointer.surface) 
     {
        /* call the unmap function */
        if (input->pointer.surface->unmap)
          input->pointer.surface->unmap(input->pointer.surface);
     }

   input->pointer.surface = ews;

   /* if we don't have a pointer surface, we are done here */
   if (!ews) return;

   /* set the destroy listener */
   wl_signal_add(&ews->wl.destroy_signal, 
                 &input->pointer.surface_destroy);

   /* set some properties on this surface */
   ews->unmap = _e_comp_wl_pointer_unmap;
   ews->configure = _e_comp_wl_pointer_configure;
   ews->input = input;

   /* update input structure with new values */
   input->pointer.hot.x = x;
   input->pointer.hot.y = y;

   if (&ews->buffer_reference)
     {
        E_Wayland_Buffer *buf;

        if ((buf = ews->buffer_reference.buffer))
          {
             Evas_Coord bw = 0, bh = 0;

             bw = buf->w;
             bh = buf->h;

             /* configure the pointer surface */
             _e_comp_wl_pointer_configure(ews, 0, 0, bw, bh);
          }
     }
}

static void 
_e_comp_wl_pointer_cb_release(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

/* keyboard interface functions */
static void 
_e_comp_wl_keyboard_cb_release(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

/* touch interface functions */
static void 
_e_comp_wl_touch_cb_release(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

/* region interface functions */
static void 
_e_comp_wl_region_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

static void 
_e_comp_wl_region_cb_add(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h)
{
   E_Wayland_Region *ewr = NULL;

   /* try to cast the resource data to our region structure */
   if (!(ewr = wl_resource_get_user_data(resource))) return;

   /* tell pixman to union this region with any previous one */
   pixman_region32_union_rect(&ewr->region, &ewr->region, x, y, w, h);
}

static void 
_e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h)
{
   E_Wayland_Region *ewr = NULL;
   pixman_region32_t region;

   /* try to cast the resource data to our region structure */
   if (!(ewr = wl_resource_get_user_data(resource))) return;

   /* ask pixman to create a new temporary rect */
   pixman_region32_init_rect(&region, x, y, w, h);

   /* ask pixman to subtract this temp rect from the existing region */
   pixman_region32_subtract(&ewr->region, &ewr->region, &region);

   /* tell pixman we are finished with the temporary rect */
   pixman_region32_fini(&region);
}

/* surface functions */
static void 
_e_comp_wl_surface_cb_pending_buffer_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Wayland_Surface *ews = NULL;

   /* try to get the surface from this listener */
   if (!(ews = container_of(listener, E_Wayland_Surface, 
                            pending.buffer_destroy)))
     return;

   /* set surface pending buffer to null */
   ews->pending.buffer = NULL;
}

static void 
_e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource)
{
   E_Wayland_Surface_Frame_Callback *cb = NULL;

   /* try to cast the resource data to our surface frame callback structure */
   if (!(cb = wl_resource_get_user_data(resource))) return;

   wl_list_remove(&cb->wl.link);

   /* free the allocated callback structure */
   E_FREE(cb);
}

static void 
_e_comp_wl_surface_buffer_reference(E_Wayland_Buffer_Reference *ref, E_Wayland_Buffer *buffer)
{
   if ((ref->buffer) && (buffer != ref->buffer))
     {
        ref->buffer->busy_count--;
        if (ref->buffer->busy_count == 0)
          wl_resource_queue_event(ref->buffer->wl.resource, WL_BUFFER_RELEASE);
        wl_list_remove(&ref->destroy_listener.link);
     }

   if ((buffer) && (buffer != ref->buffer))
     {
        buffer->busy_count++;
        wl_signal_add(&buffer->wl.destroy_signal, &ref->destroy_listener);
     }

   ref->buffer = buffer;
   ref->destroy_listener.notify = 
     _e_comp_wl_surface_buffer_reference_cb_destroy;
}

static void 
_e_comp_wl_surface_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Wayland_Buffer_Reference *ref;

   /* try to get the surface from this listener */
   ref = container_of(listener, E_Wayland_Buffer_Reference, destroy_listener);
   if (!ref) return;

   /* set referenced buffer to null */
   ref->buffer = NULL;
}

static E_Wayland_Buffer *
_e_comp_wl_surface_buffer_resource(struct wl_resource *resource)
{
   E_Wayland_Buffer *buffer;
   struct wl_listener *listener;

   listener = 
     wl_resource_get_destroy_listener(resource, 
                                      _e_comp_wl_surface_buffer_cb_destroy);
   if (listener)
     buffer = container_of(listener, E_Wayland_Buffer, wl.destroy_listener);
   else
     {
        buffer = E_NEW_RAW(E_Wayland_Buffer, 1);
        memset(buffer, 0, sizeof(*buffer));
        buffer->wl.resource = resource;
        wl_signal_init(&buffer->wl.destroy_signal);
        buffer->wl.destroy_listener.notify = 
          _e_comp_wl_surface_buffer_cb_destroy;
        wl_resource_add_destroy_listener(resource, 
                                         &buffer->wl.destroy_listener);
     }

   return buffer;
}

static void 
_e_comp_wl_surface_buffer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Wayland_Buffer *buffer;

   buffer = container_of(listener, E_Wayland_Buffer, wl.destroy_listener);
   wl_signal_emit(&buffer->wl.destroy_signal, buffer);
   E_FREE(buffer);
}

/* surface interface functionss */
static void 
_e_comp_wl_surface_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   wl_resource_destroy(resource);
}

static void 
_e_comp_wl_surface_cb_attach(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *buffer_resource, int x, int y)
{
   E_Wayland_Surface *ews = NULL;
   E_Wayland_Buffer *buffer = NULL;

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   if (buffer_resource) 
     buffer = _e_comp_wl_surface_buffer_resource(buffer_resource);

   /* reference any existing buffers */
   _e_comp_wl_surface_buffer_reference(&ews->buffer_reference, buffer);

   /* if we are setting a null buffer, then unmap the surface */
   if (!buffer)
     {
        if (ews->mapped)
          {
             if (ews->unmap) ews->unmap(ews);
          }
     }

   /* if we already have a pending buffer, remove the listener */
   if (ews->pending.buffer)
     wl_list_remove(&ews->pending.buffer_destroy.link);

   /* set some pending values */
   ews->pending.x = x;
   ews->pending.y = y;
   ews->pending.buffer = buffer;
//   if (buffer)
   ews->pending.new_buffer = EINA_TRUE;

   /* if we were given a buffer, initialize the destroy signal */
   if (buffer)
     wl_signal_add(&buffer->wl.destroy_signal, &ews->pending.buffer_destroy);
}

static void 
_e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int x, int y, int w, int h)
{
   E_Wayland_Surface *ews = NULL;

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   /* tell pixman to add this damage to pending */
   pixman_region32_union_rect(&ews->pending.damage, &ews->pending.damage, 
                              x, y, w, h);
}

static void 
_e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, unsigned int callback)
{
   E_Wayland_Surface *ews = NULL;
   E_Wayland_Surface_Frame_Callback *cb = NULL;

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   /* try to allocate space for a new frame callback */
   if (!(cb = E_NEW(E_Wayland_Surface_Frame_Callback, 1)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   cb->wl.resource = 
     wl_resource_create(client, &wl_callback_interface, 1, callback);
   wl_resource_set_implementation(cb->wl.resource, NULL, cb, 
                                  _e_comp_wl_surface_cb_frame_destroy);

   /* add this callback to the surface list of pending frames */
   wl_list_insert(ews->pending.frames.prev, &cb->wl.link);
}

static void 
_e_comp_wl_surface_cb_opaque_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
   E_Wayland_Surface *ews = NULL;

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   if (region_resource)
     {
        E_Wayland_Region *ewr = NULL;

        /* copy this region to the pending opaque region */
        if ((ewr = wl_resource_get_user_data(region_resource)))
          pixman_region32_copy(&ews->pending.opaque, &ewr->region);
     }
   else
     {
        /* tell pixman we are finished with this region */
        pixman_region32_fini(&ews->pending.opaque);

        /* reinitalize the pending opaque region */
        pixman_region32_init(&ews->pending.opaque);
     }
}

static void 
_e_comp_wl_surface_cb_input_region_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, struct wl_resource *region_resource)
{
   E_Wayland_Surface *ews = NULL;

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   if (region_resource)
     {
        E_Wayland_Region *ewr = NULL;

        /* copy this region to the pending input region */
        if ((ewr = wl_resource_get_user_data(region_resource)))
          pixman_region32_copy(&ews->pending.input, &ewr->region);
     }
   else
     {
        /* tell pixman we are finished with this region */
        pixman_region32_fini(&ews->pending.input);

        /* reinitalize the pending input region */
        pixman_region32_init_rect(&ews->pending.input, INT32_MIN, INT32_MIN, 
                                  UINT32_MAX, UINT32_MAX);
     }
}

static void 
_e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   E_Wayland_Surface *ews = NULL;
   Evas_Coord bw = 0, bh = 0;
   pixman_region32_t opaque;
   pixman_box32_t *rects;
   int n = 0;

   /* FIXME */

   /* try to cast the resource data to our surface structure */
   if (!(ews = wl_resource_get_user_data(resource))) return;

   /* if we have a pending buffer or a new pending buffer, attach it */
   if ((ews->pending.buffer) || (ews->pending.new_buffer))
     {
        /* reference the pending buffer */
        _e_comp_wl_surface_buffer_reference(&ews->buffer_reference, 
                                            ews->pending.buffer);

        /* if the pending buffer is NULL, unmap the surface */
        if (!ews->pending.buffer)
          {
             if (ews->mapped)
               {
                  if (ews->unmap) ews->unmap(ews);
               }
          }
        else
          {
             if (ews->obj)
               {
                  E_Wayland_Buffer *buff;
                  struct wl_shm_buffer *shm_buffer;
                  void *data;

                  buff = ews->pending.buffer;

                  shm_buffer = wl_shm_buffer_get(buff->wl.resource);

                  bw = wl_shm_buffer_get_width(shm_buffer);
                  bh = wl_shm_buffer_get_height(shm_buffer);

                  /* grab the pixel data from the buffer */
                  data = wl_shm_buffer_get_data(shm_buffer);

                  /* send the pixel data to the smart object */
                  e_surface_image_set(ews->obj, bw, bh, data);
               }
          }
     }

   /* if we have a reference to a buffer, get it's size */
   if (&ews->buffer_reference)
     {
        E_Wayland_Buffer *buff;
        struct wl_shm_buffer *shm_buffer;

        buff = ews->buffer_reference.buffer;

        shm_buffer = wl_shm_buffer_get(buff->wl.resource);
        bw = wl_shm_buffer_get_width(shm_buffer);
        bh = wl_shm_buffer_get_height(shm_buffer);
     }

   /* if we have a new pending buffer, call configure */
   if ((ews->configure) && (ews->pending.new_buffer))
//     ews->configure(ews, ews->pending.x, ews->pending.y, bw, bh);
     ews->configure(ews, ews->geometry.x, ews->geometry.y, bw, bh);

   if (ews->pending.buffer)
     wl_list_remove(&ews->pending.buffer_destroy.link);

   /* set some pending values */
   ews->pending.buffer = NULL;
   ews->pending.x = 0;
   ews->pending.y = 0;
   ews->pending.new_buffer = EINA_FALSE;

   /* set surface damage */
   pixman_region32_union(&ews->region.damage, &ews->region.damage, 
                         &ews->pending.damage);
   pixman_region32_intersect_rect(&ews->region.damage, &ews->region.damage, 
                                  0, 0, ews->geometry.w, ews->geometry.h);

   /* empty any pending damage */
   pixman_region32_fini(&ews->pending.damage);
   pixman_region32_init(&ews->pending.damage);

   /* get the extent of the damage region */
   rects = pixman_region32_rectangles(&ews->region.damage, &n);
   while (n--)
     {
        pixman_box32_t *r;

        r = &rects[n];

        /* send damages to the image */
        e_surface_damage_add(ews->obj, r->x1, r->y1, r->x2, r->y2);
     }

   /* tell pixman we are finished with this region */
   /* pixman_region32_fini(&ews->region.damage); */

   /* reinitalize the damage region */
   /* pixman_region32_init(&ews->region.damage); */

   /* calculate new opaque region */
   pixman_region32_init_rect(&opaque, 0, 0, ews->geometry.w, ews->geometry.h);
   pixman_region32_intersect(&opaque, &opaque, &ews->pending.opaque);

   /* if new opaque region is not equal to the current one, then update */
   if (!pixman_region32_equal(&opaque, &ews->region.opaque))
     {
        pixman_region32_copy(&ews->region.opaque, &opaque);
        ews->geometry.changed = EINA_TRUE;
     }

   /* tell pixman we are done with this temporary region */
   pixman_region32_fini(&opaque);

   /* clear any existing input region */
   pixman_region32_fini(&ews->region.input);

   /* initialize a new input region */
   pixman_region32_init_rect(&ews->region.input, 0, 0, 
                             ews->geometry.w, ews->geometry.h);

   /* put pending input region into new input region */
   pixman_region32_intersect(&ews->region.input, &ews->region.input, 
                             &ews->pending.input);

   /* check for valid input region */
//   if (pixman_region32_not_empty(&ews->region.input))
     {
        /* get the extent of the input region */
        rects = pixman_region32_extents(&ews->region.input);

        /* update the smart object's input region */
        if (ews->obj)
          e_surface_input_set(ews->obj, rects->x1, rects->y1, 
                              rects->x2, rects->y2);
     }

   /* put any pending frame callbacks into active list */
   wl_list_insert_list(&ews->wl.frames, &ews->pending.frames);

   /* clear list of pending frame callbacks */
   wl_list_init(&ews->pending.frames);

   /* TODO: schedule repaint ?? */
}

static void 
_e_comp_wl_surface_cb_buffer_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int transform EINA_UNUSED)
{

}

static void 
_e_comp_wl_surface_cb_buffer_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int scale EINA_UNUSED)
{

}
