<commit_before>#include "e.h"
#include "e_comp_wl.h"
#include "e_comp_wl_input.h"
#include "e_comp_wl_data.h"
#include "e_surface.h"

#define E_COMP_WL_PIXMAP_CHECK \
   if (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL) return

/* local variables */
static Eina_List *handlers = NULL;
static Eina_Hash *clients_win_hash = NULL;
static Ecore_Idle_Enterer *_client_idler = NULL;
static Eina_List *_idle_clients = NULL;

static void 
_e_comp_wl_focus_down_set(E_Client *ec)
{
   Ecore_Window win = 0;

   win = e_client_util_pwin_get(ec);
   e_bindings_mouse_grab(E_BINDING_CONTEXT_WINDOW, win);
   e_bindings_wheel_grab(E_BINDING_CONTEXT_WINDOW, win);
}

static void 
_e_comp_wl_focus_check(E_Comp *comp)
{
   E_Client *ec;

   if (stopping) return;
   ec = e_client_focused_get();
   if ((!ec) || (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL))
     e_grabinput_focus(comp->ee_win, E_FOCUS_METHOD_PASSIVE);
}

static void
_e_comp_wl_client_event_free(void *d EINA_UNUSED, void *event)
{
   E_Event_Client *ev;

   if (!(ev = event)) return;
   e_object_unref(E_OBJECT(ev->ec));
   free(ev);
}

static void 
_e_comp_wl_buffer_pending_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Client_Data *cd;

   if (!(cd = container_of(listener, E_Comp_Wl_Client_Data, 
                           pending.buffer_destroy)))
     return;

   cd->pending.buffer = NULL;
}

static void 
_e_comp_wl_buffer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Buffer *buffer;

   if (!(buffer = container_of(listener, E_Comp_Wl_Buffer, destroy_listener)))
     return;

   wl_signal_emit(&buffer->destroy_signal, buffer);
   free(buffer);
}

static E_Comp_Wl_Buffer *
_e_comp_wl_buffer_get(struct wl_resource *resource)
{
   E_Comp_Wl_Buffer *buffer;
   struct wl_listener *listener;

   listener = 
     wl_resource_get_destroy_listener(resource, _e_comp_wl_buffer_cb_destroy);

   if (listener)
     return container_of(listener, E_Comp_Wl_Buffer, destroy_listener);

   if (!(buffer = E_NEW(E_Comp_Wl_Buffer, 1))) return NULL;

   buffer->resource = resource;
   wl_signal_init(&buffer->destroy_signal);
   buffer->destroy_listener.notify = _e_comp_wl_buffer_cb_destroy;
   wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);

   return buffer;
}

static void 
_e_comp_wl_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Buffer_Ref *ref;

   if (!(ref = container_of(listener, E_Comp_Wl_Buffer_Ref, destroy_listener)))
     return;

   ref->buffer = NULL;
}

static void 
_e_comp_wl_buffer_reference(E_Comp_Wl_Buffer_Ref *ref, E_Comp_Wl_Buffer *buffer)
{
   if ((ref->buffer) && (buffer != ref->buffer))
     {
        ref->buffer->busy--;

        if (ref->buffer->busy == 0)
          wl_resource_queue_event(ref->buffer->resource, WL_BUFFER_RELEASE);

        wl_list_remove(&ref->destroy_listener.link);
     }

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

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

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, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
{
   E_Pixmap *cp;
   E_Client *ec;
   E_Comp_Wl_Buffer *buffer = NULL;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

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

   if (buffer_resource)
     {
        if (!(buffer = _e_comp_wl_buffer_get(buffer_resource)))
          {
             wl_client_post_no_memory(client);
             return;
          }
     }

   if (!ec->wl_comp_data) return;

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

   ec->wl_comp_data->pending.x = sx;
   ec->wl_comp_data->pending.y = sy;
   ec->wl_comp_data->pending.w = 0;
   ec->wl_comp_data->pending.h = 0;
   ec->wl_comp_data->pending.buffer = buffer;
   ec->wl_comp_data->pending.new_attach = EINA_TRUE;

   if (buffer)
     {
        struct wl_shm_buffer *b;

        b = wl_shm_buffer_get(buffer_resource);
        ec->wl_comp_data->pending.w = wl_shm_buffer_get_width(b);
        ec->wl_comp_data->pending.h = wl_shm_buffer_get_height(b);

        wl_signal_add(&buffer->destroy_signal, 
                      &ec->wl_comp_data->pending.buffer_destroy);
     }
}

static void 
_e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
   E_Pixmap *cp;
   E_Client *ec;
   Eina_Tiler *tmp;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   tmp = eina_tiler_new(ec->w, ec->h);
   eina_tiler_tile_size_set(tmp, 1, 1);
   eina_tiler_rect_add(tmp, &(Eina_Rectangle){x, y, w, h});

   eina_tiler_union(ec->wl_comp_data->pending.damage, tmp);

   eina_tiler_free(tmp);
}

static void 
_e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource)
{
   E_Client *ec;

   if (!(ec = wl_resource_get_user_data(resource))) return;

   if (e_object_is_del(E_OBJECT(ec))) return;
   if (!ec->wl_comp_data) return;

   /* remove this frame callback from the list */
   ec->wl_comp_data->frames = 
     eina_list_remove(ec->wl_comp_data->frames, resource);
}

static void 
_e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback)
{
   E_Pixmap *cp;
   E_Client *ec;
   struct wl_resource *res;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   /* create frame callback */
   res = wl_resource_create(client, &wl_callback_interface, 1, callback);
   if (!res)
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   wl_resource_set_implementation(res, NULL, ec, 
                                  _e_comp_wl_surface_cb_frame_destroy);

   /* add this frame callback to the client */
   ec->wl_comp_data->frames = eina_list_prepend(ec->wl_comp_data->frames, res);
}

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_Pixmap *cp;
   E_Client *ec;

   /* DBG("E_Surface Opaque Region Set"); */

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   if (region_resource)
     {
        Eina_Tiler *tmp;

        if (!(tmp = wl_resource_get_user_data(region_resource)))
          return;

        eina_tiler_union(ec->wl_comp_data->pending.opaque, tmp);
     }
   else
     {
        eina_tiler_clear(ec->wl_comp_data->pending.opaque);
        eina_tiler_rect_add(ec->wl_comp_data->pending.opaque, 
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});
     }
}

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_Pixmap *cp;
   E_Client *ec;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   if (region_resource)
     {
        Eina_Tiler *tmp;

        if (!(tmp = wl_resource_get_user_data(region_resource)))
          return;

        eina_tiler_union(ec->wl_comp_data->pending.input, tmp);
     }
   else
     {
        eina_tiler_clear(ec->wl_comp_data->pending.input);
        eina_tiler_rect_add(ec->wl_comp_data->pending.input, 
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});
     }
}

static void 
_e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   E_Pixmap *cp;
   E_Client *ec;
   Eina_Tiler *src, *tmp;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

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

   if (!ec->wl_comp_data) return;

   if (ec->wl_comp_data->pending.new_attach)
     {
        _e_comp_wl_buffer_reference(&ec->wl_comp_data->buffer_ref,  
                                    ec->wl_comp_data->pending.buffer);

        e_pixmap_resource_set(cp, ec->wl_comp_data->pending.buffer->resource);
        e_pixmap_usable_set(cp, (ec->wl_comp_data->pending.buffer != NULL));
     }

   e_pixmap_dirty(cp);
   e_pixmap_refresh(cp);

   if ((ec->wl_comp_data->shell.surface) && 
       (ec->wl_comp_data->shell.configure))
     {
        if (ec->wl_comp_data->pending.new_attach)
          {
             if ((ec->client.w != ec->wl_comp_data->pending.w) || 
                 (ec->client.h != ec->wl_comp_data->pending.h))
               ec->wl_comp_data->shell.configure(ec->wl_comp_data->shell.surface, 
                                                 ec->client.x, ec->client.y, 
                                                 ec->wl_comp_data->pending.w, 
                                                 ec->wl_comp_data->pending.h);
          }
     }

   if (!ec->wl_comp_data->pending.buffer)
     {
        if (ec->wl_comp_data->mapped)
          {
             if ((ec->wl_comp_data->shell.surface) && 
                 (ec->wl_comp_data->shell.unmap))
               ec->wl_comp_data->shell.unmap(ec->wl_comp_data->shell.surface);
          }
     }
   else
     {
        if (!ec->wl_comp_data->mapped)
          {
             if ((ec->wl_comp_data->shell.surface) && 
                 (ec->wl_comp_data->shell.map))
               ec->wl_comp_data->shell.map(ec->wl_comp_data->shell.surface);
          }
     }

   /* reset pending buffer */
   if (ec->wl_comp_data->pending.buffer)
     wl_list_remove(&ec->wl_comp_data->pending.buffer_destroy.link);

   ec->wl_comp_data->pending.x = 0;
   ec->wl_comp_data->pending.y = 0;
   ec->wl_comp_data->pending.w = 0;
   ec->wl_comp_data->pending.h = 0;
   ec->wl_comp_data->pending.buffer = NULL;
   ec->wl_comp_data->pending.new_attach = EINA_FALSE;

   /* handle surface damages */
   if ((!ec->comp->nocomp) && (ec->frame))
     {
        tmp = eina_tiler_new(ec->w, ec->h);
        eina_tiler_tile_size_set(tmp, 1, 1);
        eina_tiler_rect_add(tmp,
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});

        src = eina_tiler_intersection(ec->wl_comp_data->pending.damage, tmp);
        if (src)
          {
             Eina_Rectangle *rect;
             Eina_Iterator *itr;

             itr = eina_tiler_iterator_new(src);
             EINA_ITERATOR_FOREACH(itr, rect)
               {
                  e_comp_object_damage(ec->frame, 
                                       rect->x, rect->y, rect->w, rect->h);
               }
             eina_iterator_free(itr);
             eina_tiler_free(src);
          }

        eina_tiler_free(tmp);

        eina_tiler_clear(ec->wl_comp_data->pending.damage);
     }

   /* TODO !!! FIXME !!! */
   /* handle surface opaque region */

   tmp = eina_tiler_new(ec->w, ec->h);
   eina_tiler_tile_size_set(tmp, 1, 1);
   eina_tiler_rect_add(tmp, &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});

   src = eina_tiler_intersection(ec->wl_comp_data->pending.input, tmp);
   if (src)
     {
        Eina_Rectangle *rect;
        Eina_Iterator *itr;
        int i = 0;

        ec->shape_input_rects_num = 0;

        itr = eina_tiler_iterator_new(src);
        EINA_ITERATOR_FOREACH(itr, rect)
          {
             ec->shape_input_rects_num += 1;
          }

        ec->shape_input_rects = 
          malloc(sizeof(Eina_Rectangle) * ec->shape_input_rects_num);

        EINA_ITERATOR_FOREACH(itr, rect)
          {
             ec->shape_input_rects[i] = 
               *(Eina_Rectangle *)((char *)rect);

             ec->shape_input_rects[i].x = rect->x;
             ec->shape_input_rects[i].y = rect->y;
             ec->shape_input_rects[i].w = rect->w;
             ec->shape_input_rects[i].h = rect->h;

             i++;
          }

        eina_tiler_free(src);
     }

   eina_tiler_free(tmp);

   ec->changes.shape_input = EINA_TRUE;
   EC_CHANGED(ec);

/* #ifndef HAVE_WAYLAND_ONLY */
/*              e_comp_object_input_area_set(ec->frame, rect->x, rect->y,  */
/*                                           rect->w, rect->h); */
/* #endif */
}

static void 
_e_comp_wl_surface_cb_buffer_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t transform EINA_UNUSED)
{
   DBG("Surface Buffer Transform");
}

static void 
_e_comp_wl_surface_cb_buffer_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t scale EINA_UNUSED)
{
   DBG("Surface Buffer Scale");
}

static const struct wl_surface_interface _e_comp_wl_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 void 
_e_comp_wl_comp_cb_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
   E_Comp *comp;
   E_Pixmap *cp;
   struct wl_resource *res;
   uint64_t wid;
   pid_t pid;

   /* DBG("COMP_WL: Create Surface: %d", id); */

   if (!(comp = wl_resource_get_user_data(resource))) return;

   res = 
     e_comp_wl_surface_create(client, wl_resource_get_version(resource), id);
   if (!res)
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   /* get the client pid and generate a pixmap id */
   wl_client_get_credentials(client, &pid, NULL, NULL);
   wid = e_comp_wl_id_get(pid, id);

   /* see if we already have a pixmap for this surface */
   if (!(cp = e_pixmap_find(E_PIXMAP_TYPE_WL, wid)))
     {
        /* try to create a new pixmap for this surface */
        if (!(cp = e_pixmap_new(E_PIXMAP_TYPE_WL, wid)))
          {
             wl_resource_destroy(res);
             wl_resource_post_no_memory(resource);
             return;
          }
     }

   /* set reference to pixmap so we can fetch it later */
   wl_resource_set_user_data(res, cp);
}

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, int32_t x, int32_t y, int32_t w, int32_t h)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     {
        Eina_Tiler *src;

        src = eina_tiler_new(w, h);
        eina_tiler_tile_size_set(src, 1, 1);
        eina_tiler_rect_add(src, &(Eina_Rectangle){x, y, w, h});

        eina_tiler_union(tiler, src);
        eina_tiler_free(src);
     }
}

static void 
_e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     {
        Eina_Tiler *src;

        src = eina_tiler_new(w, h);
        eina_tiler_tile_size_set(src, 1, 1);
        eina_tiler_rect_add(src, &(Eina_Rectangle){x, y, w, h});

        eina_tiler_subtract(tiler, src);
        eina_tiler_free(src);
     }
}

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 void 
_e_comp_wl_comp_cb_region_destroy(struct wl_resource *resource)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     eina_tiler_free(tiler);
}

static void 
_e_comp_wl_comp_cb_region_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
   E_Comp  *c;
   Eina_Tiler *tiler;
   struct wl_resource *res;

   if (!(c = e_comp_get(NULL)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   if (!(tiler = eina_tiler_new(c->man->w, c->man->h)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   eina_tiler_tile_size_set(tiler, 1, 1);
   eina_tiler_rect_add(tiler, 
                       &(Eina_Rectangle){0, 0, c->man->w, c->man->h});

   if (!(res = wl_resource_create(client, &wl_region_interface, 1, id)))
     {
        eina_tiler_free(tiler);
        wl_resource_post_no_memory(resource);
        return;
     }

   wl_resource_set_implementation(res, &_e_region_interface, tiler, 
                                  _e_comp_wl_comp_cb_region_destroy);
}

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

static void 
_e_comp_wl_subcomp_cb_subsurface_get(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t id EINA_UNUSED, struct wl_resource *surface_resource EINA_UNUSED, struct wl_resource *parent_resource EINA_UNUSED)
{
   /* NB: Needs New Resource */
#warning TODO Need to subcomp subsurface
}

static const struct wl_compositor_interface _e_comp_interface = 
{
   _e_comp_wl_comp_cb_surface_create,
   _e_comp_wl_comp_cb_region_create
};

static const struct wl_subcompositor_interface _e_subcomp_interface = 
{
   _e_comp_wl_subcomp_cb_destroy,
   _e_comp_wl_subcomp_cb_subsurface_get
};

static void 
_e_comp_wl_cb_bind_compositor(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
   E_Comp *comp;
   struct wl_resource *res;

   if (!(comp = data)) return;

   res = 
     wl_resource_create(client, &wl_compositor_interface, MIN(version, 3), id);
   if (!res)
     {
        wl_client_post_no_memory(client);
        return;
     }

   wl_resource_set_implementation(res, &_e_comp_interface, comp, NULL);
}

static void 
_e_comp_wl_cb_bind_subcompositor(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
   E_Comp *comp;
   struct wl_resource *res;

   if (!(comp = data)) return;

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

   wl_resource_set_implementation(res, &_e_subcomp_interface, comp, NULL);
}

static void 
_e_comp_wl_cb_render_post(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, void *event EINA_UNUSED)
{
   Eina_Iterator *itr;
   E_Client *ec;

   if (!(itr = eina_hash_iterator_data_new(clients_win_hash))) return;

   EINA_ITERATOR_FOREACH(itr, ec)
     {
        struct wl_resource *cb;

        if (!ec->wl_comp_data) continue;
        EINA_LIST_FREE(ec->wl_comp_data->frames, cb)
          {
             wl_callback_send_done(cb, ecore_loop_time_get());
             wl_resource_destroy(cb);
          }

        /* post a buffer release */
        /* TODO: FIXME: We need a way to determine if the client wants to 
         * keep the buffer or not. If so, then we should Not be setting NULL 
         * here as this will essentially release the buffer */
        _e_comp_wl_buffer_reference(&ec->wl_comp_data->buffer_ref, NULL);
     }

   eina_iterator_free(itr);
}

static void 
_e_comp_wl_cb_del(E_Comp *comp)
{
   E_Comp_Wl_Data *cdata;

   cdata = comp->wl_comp_data;

   e_comp_wl_data_manager_shutdown(cdata);
   e_comp_wl_input_shutdown(cdata);

   /* remove render_post callback */
   evas_event_callback_del_full(comp->evas, EVAS_CALLBACK_RENDER_POST, 
                                _e_comp_wl_cb_render_post, NULL);

   /* delete idler to flush clients */
   if (cdata->idler) ecore_idler_del(cdata->idler);

   /* delete fd handler to listen for wayland main loop events */
   if (cdata->fd_hdlr) ecore_main_fd_handler_del(cdata->fd_hdlr);

   /* delete the wayland display */
   if (cdata->wl.disp) wl_display_destroy(cdata->wl.disp);

   free(cdata);
}

static Eina_Bool 
_e_comp_wl_cb_read(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED)
{
   E_Comp_Wl_Data *cdata;

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;
   if (!cdata->wl.disp) return ECORE_CALLBACK_RENEW;

   /* flush any pending client events */
   wl_display_flush_clients(cdata->wl.disp);

   /* dispatch any pending main loop events */
   wl_event_loop_dispatch(cdata->wl.loop, 0);

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool 
_e_comp_wl_cb_idle(void *data)
{
   E_Comp_Wl_Data *cdata;

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;
   if (!cdata->wl.disp) return ECORE_CALLBACK_RENEW;

   /* flush any pending client events */
   wl_display_flush_clients(cdata->wl.disp);

   /* dispatch any pending main loop events */
   wl_event_loop_dispatch(cdata->wl.loop, 0);

   return ECORE_CALLBACK_RENEW;
}

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

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;

   if (e_module_loading_get()) return ECORE_CALLBACK_RENEW;

   /* FIXME: make which shell to load configurable */
   if (!(mod = e_module_find("wl_desktop_shell")))
     mod = e_module_new("wl_desktop_shell");

   if (mod)
     {
        e_module_enable(mod);

        /* dispatch any pending main loop events */
        wl_event_loop_dispatch(cdata->wl.loop, 0);

        return ECORE_CALLBACK_CANCEL;
     }

   return ECORE_CALLBACK_RENEW;
}

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

   if (!(ec = data)) return EINA_TRUE;
   ec->wl_comp_data->first_draw_tmr = NULL;
   e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
   return EINA_FALSE;
}

static Eina_Bool 
_e_comp_wl_compositor_create(void)
{
   E_Comp *comp;
   E_Comp_Wl_Data *cdata;
   char buff[PATH_MAX];
   /* char *rules, *model, *layout; */
   int fd = 0;

   /* get the current compositor */
   if (!(comp = e_comp_get(NULL)))
     {
        comp = e_comp_new();
        comp->comp_type = E_PIXMAP_TYPE_WL;
        E_OBJECT_DEL_SET(comp, _e_comp_wl_cb_del);
     }

   /* check compositor type and make sure it's Wayland */
   /* if (comp->comp_type != E_PIXMAP_TYPE_WL) return EINA_FALSE; */

   cdata = E_NEW(E_Comp_Wl_Data, 1);
   comp->wl_comp_data = cdata;

   /* setup wayland display environment variable */
   snprintf(buff, sizeof(buff), "%s/wayland-0", e_ipc_socket);
   e_env_set("WAYLAND_DISPLAY", buff);

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

   if (wl_display_add_socket(cdata->wl.disp, buff))
     {
        ERR("Could not create a Wayland Display: %m");
        goto disp_err;
     }

   /* setup wayland compositor signals */
   /* NB: So far, we don't need ANY of these... */
   /* wl_signal_init(&cdata->signals.destroy); */
   /* wl_signal_init(&cdata->signals.activate); */
   /* wl_signal_init(&cdata->signals.transform); */
   /* wl_signal_init(&cdata->signals.kill); */
   /* wl_signal_init(&cdata->signals.idle); */
   /* wl_signal_init(&cdata->signals.wake); */
   /* wl_signal_init(&cdata->signals.session); */
   /* wl_signal_init(&cdata->signals.seat.created); */
   /* wl_signal_init(&cdata->signals.seat.destroyed); */
   /* wl_signal_init(&cdata->signals.seat.moved); */
   /* wl_signal_init(&cdata->signals.output.created); */
   /* wl_signal_init(&cdata->signals.output.destroyed); */
   /* wl_signal_init(&cdata->signals.output.moved); */

   /* try to add compositor to wayland display globals */
   if (!wl_global_create(cdata->wl.disp, &wl_compositor_interface, 3, 
                         comp, _e_comp_wl_cb_bind_compositor))
     {
        ERR("Could not add compositor to globals: %m");
        goto disp_err;
     }

   /* try to add subcompositor to wayland display globals */
   if (!wl_global_create(cdata->wl.disp, &wl_subcompositor_interface, 1, 
                         comp, _e_comp_wl_cb_bind_subcompositor))
     {
        ERR("Could not add compositor to globals: %m");
        goto disp_err;
     }

   /* try to init data manager */
   if (!e_comp_wl_data_manager_init(cdata))
     {
        ERR("Could not initialize data manager");
        goto disp_err;
     }

   /* try to init input (keyboard & pointer) */
   if (!e_comp_wl_input_init(cdata))
     {
        ERR("Could not initialize input");
        goto disp_err;
     }

/* #ifndef HAVE_WAYLAND_ONLY */
/*    if (getenv("DISPLAY")) */
/*      { */
/*         E_Config_XKB_Layout *ekbd; */
/*         Ecore_X_Atom xkb = 0; */
/*         Ecore_X_Window root = 0; */
/*         int len = 0; */
/*         unsigned char *dat; */

/*         if ((ekbd = e_xkb_layout_get())) */
/*           { */
/*              model = strdup(ekbd->model); */
/*              layout = strdup(ekbd->name); */
/*           } */

/*         root = ecore_x_window_root_first_get(); */
/*         xkb = ecore_x_atom_get("_XKB_RULES_NAMES"); */
/*         ecore_x_window_prop_property_get(root, xkb, ECORE_X_ATOM_STRING,  */
/*                                          1024, &dat, &len); */
/*         if ((dat) && (len > 0)) */
/*           { */
/*              rules = (char *)dat; */
/*              dat += strlen((const char *)dat) + 1; */
/*              if (!model) model = strdup((const char *)dat); */
/*              dat += strlen((const char *)dat) + 1; */
/*              if (!layout) layout = strdup((const char *)dat); */
/*           } */
/*      } */
/* #endif */

   /* fallback */
   /* if (!rules) rules = strdup("evdev"); */
   /* if (!model) model = strdup("pc105"); */
   /* if (!layout) layout = strdup("us"); */

   /* update compositor keymap */
   /* e_comp_wl_input_keymap_set(cdata, rules, model, layout); */

   /* TODO: init text backend */

   /* initialize shm mechanism */
   wl_display_init_shm(cdata->wl.disp);

   /* check for gl rendering */
   if ((e_comp_gl_get()) && 
       (e_comp_config_get()->engine == E_COMP_ENGINE_GL))
     {
        /* TODO: setup gl ? */
     }

   /* get the wayland display's event loop */
   cdata->wl.loop= wl_display_get_event_loop(cdata->wl.disp);

   /* get the file descriptor of the main loop */
   fd = wl_event_loop_get_fd(cdata->wl.loop);

   /* add an fd handler to listen for wayland main loop events */
   cdata->fd_hdlr = 
     ecore_main_fd_handler_add(fd, ECORE_FD_READ | ECORE_FD_WRITE, 
                               _e_comp_wl_cb_read, cdata, NULL, NULL);

   /* add an idler to flush clients */
   cdata->idler = ecore_idle_enterer_add(_e_comp_wl_cb_idle, cdata);

   /* setup module idler to load shell module */
   ecore_idler_add(_e_comp_wl_cb_module_idle, cdata);

   /* add a render post callback so we can send frame_done to the surface */
   evas_event_callback_add(comp->evas, EVAS_CALLBACK_RENDER_POST, 
                           _e_comp_wl_cb_render_post, NULL);

   return EINA_TRUE;

disp_err:
   e_env_unset("WAYLAND_DISPLAY");
   return EINA_FALSE;
}

static Eina_Bool 
_e_comp_wl_client_idler(void *data EINA_UNUSED)
{
   E_Client *ec;
   E_Comp *comp;
   const Eina_List *l;

   EINA_LIST_FREE(_idle_clients, ec)
     {
        if ((e_object_is_del(E_OBJECT(ec))) || (!ec->wl_comp_data)) continue;

        ec->post_move = 0;
        ec->post_resize = 0;
     }

   EINA_LIST_FOREACH(e_comp_list(), l, comp)
     {
        if ((comp->wl_comp_data->restack) && (!comp->new_clients))
          {
             e_hints_client_stacking_set();
             comp->wl_comp_data->restack = EINA_FALSE;
          }
     }

   _client_idler = NULL;
   return EINA_FALSE;
}

static void 
_e_comp_wl_client_idler_add(E_Client *ec)
{
   if (!_client_idler) 
     _client_idler = ecore_idle_enterer_add(_e_comp_wl_client_idler, NULL);

   if (!ec) CRI("ACK!");

   if (!eina_list_data_find(_idle_clients, ec))
     _idle_clients = eina_list_append(_idle_clients, ec);
}

static void 
_e_comp_wl_client_priority_adjust(int pid, int set, int adj, Eina_Bool use_adj, Eina_Bool adj_child, Eina_Bool do_child)
{
   int n;

   n = set;
   if (use_adj) n = (getpriority(PRIO_PROCESS, pid) + adj);
   setpriority(PRIO_PROCESS, pid, n);

   if (do_child)
     {
        Eina_List *files;
        char *file, buff[PATH_MAX];
        FILE *f;
        int pid2, ppid;

        files = ecore_file_ls("/proc");
        EINA_LIST_FREE(files, file)
          {
             if (isdigit(file[0]))
               {
                  snprintf(buff, sizeof(buff), "/proc/%s/stat", file);
                  if ((f = fopen(buff, "r")))
                    {
                       pid2 = -1;
                       ppid = -1;
                       if (fscanf(f, "%i %*s %*s %i %*s", &pid2, &ppid) == 2)
                         {
                            fclose(f);
                            if (ppid == pid)
                              {
                                 if (adj_child)
                                   _e_comp_wl_client_priority_adjust(pid2, set, 
                                                                     adj, EINA_TRUE,
                                                                     adj_child, do_child);
                                 else
                                   _e_comp_wl_client_priority_adjust(pid2, set, 
                                                                     adj, use_adj,
                                                                     adj_child, do_child);
                              }
                         }
                       else 
                         fclose(f);
                    }
               }
             free(file);
          }
     }
}

static void 
_e_comp_wl_client_priority_raise(E_Client *ec)
{
   if (ec->netwm.pid <= 0) return;
   if (ec->netwm.pid == getpid()) return;
   _e_comp_wl_client_priority_adjust(ec->netwm.pid, 
                                     e_config->priority - 1, -1, 
                                     EINA_FALSE, EINA_TRUE, EINA_FALSE);
}

static void 
_e_comp_wl_client_priority_normal(E_Client *ec)
{
   if (ec->netwm.pid <= 0) return;
   if (ec->netwm.pid == getpid()) return;
   _e_comp_wl_client_priority_adjust(ec->netwm.pid, e_config->priority, 1, 
                                     EINA_FALSE, EINA_TRUE, EINA_FALSE);
}

static void 
_e_comp_wl_evas_cb_show(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *tmp;
   Eina_List *l;

   if (!(ec = data)) return;

   if (!ec->override) 
     e_hints_window_visible_set(ec);

   if (ec->wl_comp_data->frame_update)
     ec->wl_comp_data->frame_update = EINA_FALSE;

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

static void 
_e_comp_wl_evas_cb_hide(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *tmp;
   Eina_List *l;

   if (!(ec = data)) return;

   EINA_LIST_FOREACH(ec->e.state.video_child, l, tmp)
     evas_object_hide(tmp->frame);
}

static void 
_e_comp_wl_evas_cb_mouse_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_In *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial;

   ev = event;
   if (!(ec = data)) return;
   if (!ec->wl_comp_data) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_enter(res, serial, ec->wl_comp_data->surface, 
                              wl_fixed_from_int(ev->canvas.x), 
                              wl_fixed_from_int(ev->canvas.y));
     }
}

static void 
_e_comp_wl_evas_cb_mouse_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial;

   if (!(ec = data)) return;
   if (!ec->wl_comp_data) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_leave(res, serial, ec->wl_comp_data->surface);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Down *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, btn;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   switch (ev->button)
     {
      case 1:
        btn = BTN_LEFT;
        break;
      case 2:
        btn = BTN_MIDDLE;
        break;
      case 3:
        btn = BTN_RIGHT;
        break;
      default:
        btn = ev->button;
        break;
     }

   ec->comp->wl_comp_data->ptr.button = btn;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_button(res, serial, ev->timestamp, btn, 
                               WL_POINTER_BUTTON_STATE_PRESSED);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Up *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, btn;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   switch (ev->button)
     {
      case 1:
        btn = BTN_LEFT;
        break;
      case 2:
        btn = BTN_MIDDLE;
        break;
      case 3:
        btn = BTN_RIGHT;
        break;
      default:
        btn = ev->button;
        break;
     }

   ec->comp->wl_comp_data->resize.resource = NULL;
   ec->comp->wl_comp_data->ptr.button = btn;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_button(res, serial, ev->timestamp, btn, 
                               WL_POINTER_BUTTON_STATE_RELEASED);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Wheel *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t axis, dir;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   if (ev->direction == 0)
     axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
   else
     axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;

   if (ev->z < 0)
     dir = -wl_fixed_from_int(abs(ev->z));
   else
     dir = wl_fixed_from_int(ev->z);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_axis(res, ev->timestamp, axis, dir);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Move *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   ec->comp->wl_comp_data->ptr.x = 
     wl_fixed_from_int(ev->cur.canvas.x - ec->client.x);
   ec->comp->wl_comp_data->ptr.y = 
     wl_fixed_from_int(ev->cur.canvas.y - ec->client.y);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_motion(res, ev->timestamp, 
                               ec->comp->wl_comp_data->ptr.x, 
                               ec->comp->wl_comp_data->ptr.y);
     }
}

static void 
_e_comp_wl_evas_cb_key_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   E_Comp_Wl_Data *cdata;
   Evas_Event_Key_Down *ev;
   uint32_t serial, *end, *k, keycode;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   if (!ec->focused) return;

   keycode = (ev->keycode - 8);
   if (!(cdata = ec->comp->wl_comp_data)) return;

   end = (uint32_t *)cdata->kbd.keys.data + cdata->kbd.keys.size;

   for (k = cdata->kbd.keys.data; k < end; k++)
     {
        /* ignore server-generated key repeats */
        if (*k == keycode) return;
     }

   cdata->kbd.keys.size = end - (uint32_t *)cdata->kbd.keys.data;
   k = wl_array_add(&cdata->kbd.keys, sizeof(*k));
   *k = keycode;

   /* update modifier state */
   e_comp_wl_input_keyboard_state_update(cdata, keycode, EINA_TRUE);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->kbd.resources, l, res)
     {
        if (wl_resource_get_client(res) != wc) continue;
        wl_keyboard_send_key(res, serial, ev->timestamp, 
                             keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
     }
}

static void 
_e_comp_wl_evas_cb_key_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   E_Comp_Wl_Data *cdata;
   Evas_Event_Key_Up *ev;
   uint32_t serial, *end, *k, keycode;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   if (!ec->focused) return;

   keycode = (ev->keycode - 8);
   if (!(cdata = ec->comp->wl_comp_data)) return;

   end = (uint32_t *)cdata->kbd.keys.data + cdata->kbd.keys.size;
   for (k = cdata->kbd.keys.data; k < end; k++)
     if (*k == keycode) *k = *--end;

   cdata->kbd.keys.size = end - (uint32_t *)cdata->kbd.keys.data;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(cdata->wl.disp);
   EINA_LIST_FOREACH(cdata->kbd.resources, l, res)
     {
        if (wl_resource_get_client(res) != wc) continue;
        wl_keyboard_send_key(res, serial, ev->timestamp, 
                             keycode, WL_KEYBOARD_KEY_STATE_RELEASED);
     }

   /* update modifier state */
   e_comp_wl_input_keyboard_state_update(cdata, keycode, EINA_FALSE);
}

static void 
_e_comp_wl_evas_cb_focus_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *focused;
   E_Comp_Wl_Data *cdata;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, *k;

   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   /* block refocus attempts on iconic clients
    * these result from iconifying a client during a grab */<commit_msg>fix crash and memory leak when press and release any key repeatedly.<commit_after>#include "e.h"
#include "e_comp_wl.h"
#include "e_comp_wl_input.h"
#include "e_comp_wl_data.h"
#include "e_surface.h"

#define E_COMP_WL_PIXMAP_CHECK \
   if (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL) return

/* local variables */
static Eina_List *handlers = NULL;
static Eina_Hash *clients_win_hash = NULL;
static Ecore_Idle_Enterer *_client_idler = NULL;
static Eina_List *_idle_clients = NULL;

static void 
_e_comp_wl_focus_down_set(E_Client *ec)
{
   Ecore_Window win = 0;

   win = e_client_util_pwin_get(ec);
   e_bindings_mouse_grab(E_BINDING_CONTEXT_WINDOW, win);
   e_bindings_wheel_grab(E_BINDING_CONTEXT_WINDOW, win);
}

static void 
_e_comp_wl_focus_check(E_Comp *comp)
{
   E_Client *ec;

   if (stopping) return;
   ec = e_client_focused_get();
   if ((!ec) || (e_pixmap_type_get(ec->pixmap) != E_PIXMAP_TYPE_WL))
     e_grabinput_focus(comp->ee_win, E_FOCUS_METHOD_PASSIVE);
}

static void
_e_comp_wl_client_event_free(void *d EINA_UNUSED, void *event)
{
   E_Event_Client *ev;

   if (!(ev = event)) return;
   e_object_unref(E_OBJECT(ev->ec));
   free(ev);
}

static void 
_e_comp_wl_buffer_pending_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Client_Data *cd;

   if (!(cd = container_of(listener, E_Comp_Wl_Client_Data, 
                           pending.buffer_destroy)))
     return;

   cd->pending.buffer = NULL;
}

static void 
_e_comp_wl_buffer_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Buffer *buffer;

   if (!(buffer = container_of(listener, E_Comp_Wl_Buffer, destroy_listener)))
     return;

   wl_signal_emit(&buffer->destroy_signal, buffer);
   free(buffer);
}

static E_Comp_Wl_Buffer *
_e_comp_wl_buffer_get(struct wl_resource *resource)
{
   E_Comp_Wl_Buffer *buffer;
   struct wl_listener *listener;

   listener = 
     wl_resource_get_destroy_listener(resource, _e_comp_wl_buffer_cb_destroy);

   if (listener)
     return container_of(listener, E_Comp_Wl_Buffer, destroy_listener);

   if (!(buffer = E_NEW(E_Comp_Wl_Buffer, 1))) return NULL;

   buffer->resource = resource;
   wl_signal_init(&buffer->destroy_signal);
   buffer->destroy_listener.notify = _e_comp_wl_buffer_cb_destroy;
   wl_resource_add_destroy_listener(resource, &buffer->destroy_listener);

   return buffer;
}

static void 
_e_comp_wl_buffer_reference_cb_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
   E_Comp_Wl_Buffer_Ref *ref;

   if (!(ref = container_of(listener, E_Comp_Wl_Buffer_Ref, destroy_listener)))
     return;

   ref->buffer = NULL;
}

static void 
_e_comp_wl_buffer_reference(E_Comp_Wl_Buffer_Ref *ref, E_Comp_Wl_Buffer *buffer)
{
   if ((ref->buffer) && (buffer != ref->buffer))
     {
        ref->buffer->busy--;

        if (ref->buffer->busy == 0)
          wl_resource_queue_event(ref->buffer->resource, WL_BUFFER_RELEASE);

        wl_list_remove(&ref->destroy_listener.link);
     }

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

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

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, struct wl_resource *resource, struct wl_resource *buffer_resource, int32_t sx, int32_t sy)
{
   E_Pixmap *cp;
   E_Client *ec;
   E_Comp_Wl_Buffer *buffer = NULL;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

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

   if (buffer_resource)
     {
        if (!(buffer = _e_comp_wl_buffer_get(buffer_resource)))
          {
             wl_client_post_no_memory(client);
             return;
          }
     }

   if (!ec->wl_comp_data) return;

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

   ec->wl_comp_data->pending.x = sx;
   ec->wl_comp_data->pending.y = sy;
   ec->wl_comp_data->pending.w = 0;
   ec->wl_comp_data->pending.h = 0;
   ec->wl_comp_data->pending.buffer = buffer;
   ec->wl_comp_data->pending.new_attach = EINA_TRUE;

   if (buffer)
     {
        struct wl_shm_buffer *b;

        b = wl_shm_buffer_get(buffer_resource);
        ec->wl_comp_data->pending.w = wl_shm_buffer_get_width(b);
        ec->wl_comp_data->pending.h = wl_shm_buffer_get_height(b);

        wl_signal_add(&buffer->destroy_signal, 
                      &ec->wl_comp_data->pending.buffer_destroy);
     }
}

static void 
_e_comp_wl_surface_cb_damage(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
   E_Pixmap *cp;
   E_Client *ec;
   Eina_Tiler *tmp;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   tmp = eina_tiler_new(ec->w, ec->h);
   eina_tiler_tile_size_set(tmp, 1, 1);
   eina_tiler_rect_add(tmp, &(Eina_Rectangle){x, y, w, h});

   eina_tiler_union(ec->wl_comp_data->pending.damage, tmp);

   eina_tiler_free(tmp);
}

static void 
_e_comp_wl_surface_cb_frame_destroy(struct wl_resource *resource)
{
   E_Client *ec;

   if (!(ec = wl_resource_get_user_data(resource))) return;

   if (e_object_is_del(E_OBJECT(ec))) return;
   if (!ec->wl_comp_data) return;

   /* remove this frame callback from the list */
   ec->wl_comp_data->frames = 
     eina_list_remove(ec->wl_comp_data->frames, resource);
}

static void 
_e_comp_wl_surface_cb_frame(struct wl_client *client, struct wl_resource *resource, uint32_t callback)
{
   E_Pixmap *cp;
   E_Client *ec;
   struct wl_resource *res;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   /* create frame callback */
   res = wl_resource_create(client, &wl_callback_interface, 1, callback);
   if (!res)
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   wl_resource_set_implementation(res, NULL, ec, 
                                  _e_comp_wl_surface_cb_frame_destroy);

   /* add this frame callback to the client */
   ec->wl_comp_data->frames = eina_list_prepend(ec->wl_comp_data->frames, res);
}

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_Pixmap *cp;
   E_Client *ec;

   /* DBG("E_Surface Opaque Region Set"); */

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   if (region_resource)
     {
        Eina_Tiler *tmp;

        if (!(tmp = wl_resource_get_user_data(region_resource)))
          return;

        eina_tiler_union(ec->wl_comp_data->pending.opaque, tmp);
     }
   else
     {
        eina_tiler_clear(ec->wl_comp_data->pending.opaque);
        eina_tiler_rect_add(ec->wl_comp_data->pending.opaque, 
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});
     }
}

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_Pixmap *cp;
   E_Client *ec;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

   if ((!ec) || (e_object_is_del(E_OBJECT(ec)))) return;
   if (!ec->wl_comp_data) return;

   if (region_resource)
     {
        Eina_Tiler *tmp;

        if (!(tmp = wl_resource_get_user_data(region_resource)))
          return;

        eina_tiler_union(ec->wl_comp_data->pending.input, tmp);
     }
   else
     {
        eina_tiler_clear(ec->wl_comp_data->pending.input);
        eina_tiler_rect_add(ec->wl_comp_data->pending.input, 
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});
     }
}

static void 
_e_comp_wl_surface_cb_commit(struct wl_client *client EINA_UNUSED, struct wl_resource *resource)
{
   E_Pixmap *cp;
   E_Client *ec;
   Eina_Tiler *src, *tmp;

   if (!(cp = wl_resource_get_user_data(resource))) return;

   /* try to find the E client for this surface */
   if (!(ec = e_pixmap_client_get(cp)))
     ec = e_pixmap_find_client(E_PIXMAP_TYPE_WL, e_pixmap_window_get(cp));

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

   if (!ec->wl_comp_data) return;

   if (ec->wl_comp_data->pending.new_attach)
     {
        _e_comp_wl_buffer_reference(&ec->wl_comp_data->buffer_ref,  
                                    ec->wl_comp_data->pending.buffer);

        e_pixmap_resource_set(cp, ec->wl_comp_data->pending.buffer->resource);
        e_pixmap_usable_set(cp, (ec->wl_comp_data->pending.buffer != NULL));
     }

   e_pixmap_dirty(cp);
   e_pixmap_refresh(cp);

   if ((ec->wl_comp_data->shell.surface) && 
       (ec->wl_comp_data->shell.configure))
     {
        if (ec->wl_comp_data->pending.new_attach)
          {
             if ((ec->client.w != ec->wl_comp_data->pending.w) || 
                 (ec->client.h != ec->wl_comp_data->pending.h))
               ec->wl_comp_data->shell.configure(ec->wl_comp_data->shell.surface, 
                                                 ec->client.x, ec->client.y, 
                                                 ec->wl_comp_data->pending.w, 
                                                 ec->wl_comp_data->pending.h);
          }
     }

   if (!ec->wl_comp_data->pending.buffer)
     {
        if (ec->wl_comp_data->mapped)
          {
             if ((ec->wl_comp_data->shell.surface) && 
                 (ec->wl_comp_data->shell.unmap))
               ec->wl_comp_data->shell.unmap(ec->wl_comp_data->shell.surface);
          }
     }
   else
     {
        if (!ec->wl_comp_data->mapped)
          {
             if ((ec->wl_comp_data->shell.surface) && 
                 (ec->wl_comp_data->shell.map))
               ec->wl_comp_data->shell.map(ec->wl_comp_data->shell.surface);
          }
     }

   /* reset pending buffer */
   if (ec->wl_comp_data->pending.buffer)
     wl_list_remove(&ec->wl_comp_data->pending.buffer_destroy.link);

   ec->wl_comp_data->pending.x = 0;
   ec->wl_comp_data->pending.y = 0;
   ec->wl_comp_data->pending.w = 0;
   ec->wl_comp_data->pending.h = 0;
   ec->wl_comp_data->pending.buffer = NULL;
   ec->wl_comp_data->pending.new_attach = EINA_FALSE;

   /* handle surface damages */
   if ((!ec->comp->nocomp) && (ec->frame))
     {
        tmp = eina_tiler_new(ec->w, ec->h);
        eina_tiler_tile_size_set(tmp, 1, 1);
        eina_tiler_rect_add(tmp,
                            &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});

        src = eina_tiler_intersection(ec->wl_comp_data->pending.damage, tmp);
        if (src)
          {
             Eina_Rectangle *rect;
             Eina_Iterator *itr;

             itr = eina_tiler_iterator_new(src);
             EINA_ITERATOR_FOREACH(itr, rect)
               {
                  e_comp_object_damage(ec->frame, 
                                       rect->x, rect->y, rect->w, rect->h);
               }
             eina_iterator_free(itr);
             eina_tiler_free(src);
          }

        eina_tiler_free(tmp);

        eina_tiler_clear(ec->wl_comp_data->pending.damage);
     }

   /* TODO !!! FIXME !!! */
   /* handle surface opaque region */

   tmp = eina_tiler_new(ec->w, ec->h);
   eina_tiler_tile_size_set(tmp, 1, 1);
   eina_tiler_rect_add(tmp, &(Eina_Rectangle){0, 0, ec->client.w, ec->client.h});

   src = eina_tiler_intersection(ec->wl_comp_data->pending.input, tmp);
   if (src)
     {
        Eina_Rectangle *rect;
        Eina_Iterator *itr;
        int i = 0;

        ec->shape_input_rects_num = 0;

        itr = eina_tiler_iterator_new(src);
        EINA_ITERATOR_FOREACH(itr, rect)
          {
             ec->shape_input_rects_num += 1;
          }

        ec->shape_input_rects = 
          malloc(sizeof(Eina_Rectangle) * ec->shape_input_rects_num);

        EINA_ITERATOR_FOREACH(itr, rect)
          {
             ec->shape_input_rects[i] = 
               *(Eina_Rectangle *)((char *)rect);

             ec->shape_input_rects[i].x = rect->x;
             ec->shape_input_rects[i].y = rect->y;
             ec->shape_input_rects[i].w = rect->w;
             ec->shape_input_rects[i].h = rect->h;

             i++;
          }

        eina_tiler_free(src);
     }

   eina_tiler_free(tmp);

   ec->changes.shape_input = EINA_TRUE;
   EC_CHANGED(ec);

/* #ifndef HAVE_WAYLAND_ONLY */
/*              e_comp_object_input_area_set(ec->frame, rect->x, rect->y,  */
/*                                           rect->w, rect->h); */
/* #endif */
}

static void 
_e_comp_wl_surface_cb_buffer_transform_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t transform EINA_UNUSED)
{
   DBG("Surface Buffer Transform");
}

static void 
_e_comp_wl_surface_cb_buffer_scale_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, int32_t scale EINA_UNUSED)
{
   DBG("Surface Buffer Scale");
}

static const struct wl_surface_interface _e_comp_wl_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 void 
_e_comp_wl_comp_cb_surface_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
   E_Comp *comp;
   E_Pixmap *cp;
   struct wl_resource *res;
   uint64_t wid;
   pid_t pid;

   /* DBG("COMP_WL: Create Surface: %d", id); */

   if (!(comp = wl_resource_get_user_data(resource))) return;

   res = 
     e_comp_wl_surface_create(client, wl_resource_get_version(resource), id);
   if (!res)
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   /* get the client pid and generate a pixmap id */
   wl_client_get_credentials(client, &pid, NULL, NULL);
   wid = e_comp_wl_id_get(pid, id);

   /* see if we already have a pixmap for this surface */
   if (!(cp = e_pixmap_find(E_PIXMAP_TYPE_WL, wid)))
     {
        /* try to create a new pixmap for this surface */
        if (!(cp = e_pixmap_new(E_PIXMAP_TYPE_WL, wid)))
          {
             wl_resource_destroy(res);
             wl_resource_post_no_memory(resource);
             return;
          }
     }

   /* set reference to pixmap so we can fetch it later */
   wl_resource_set_user_data(res, cp);
}

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, int32_t x, int32_t y, int32_t w, int32_t h)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     {
        Eina_Tiler *src;

        src = eina_tiler_new(w, h);
        eina_tiler_tile_size_set(src, 1, 1);
        eina_tiler_rect_add(src, &(Eina_Rectangle){x, y, w, h});

        eina_tiler_union(tiler, src);
        eina_tiler_free(src);
     }
}

static void 
_e_comp_wl_region_cb_subtract(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, int32_t x, int32_t y, int32_t w, int32_t h)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     {
        Eina_Tiler *src;

        src = eina_tiler_new(w, h);
        eina_tiler_tile_size_set(src, 1, 1);
        eina_tiler_rect_add(src, &(Eina_Rectangle){x, y, w, h});

        eina_tiler_subtract(tiler, src);
        eina_tiler_free(src);
     }
}

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 void 
_e_comp_wl_comp_cb_region_destroy(struct wl_resource *resource)
{
   Eina_Tiler *tiler;

   if ((tiler = wl_resource_get_user_data(resource)))
     eina_tiler_free(tiler);
}

static void 
_e_comp_wl_comp_cb_region_create(struct wl_client *client, struct wl_resource *resource, uint32_t id)
{
   E_Comp  *c;
   Eina_Tiler *tiler;
   struct wl_resource *res;

   if (!(c = e_comp_get(NULL)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   if (!(tiler = eina_tiler_new(c->man->w, c->man->h)))
     {
        wl_resource_post_no_memory(resource);
        return;
     }

   eina_tiler_tile_size_set(tiler, 1, 1);
   eina_tiler_rect_add(tiler, 
                       &(Eina_Rectangle){0, 0, c->man->w, c->man->h});

   if (!(res = wl_resource_create(client, &wl_region_interface, 1, id)))
     {
        eina_tiler_free(tiler);
        wl_resource_post_no_memory(resource);
        return;
     }

   wl_resource_set_implementation(res, &_e_region_interface, tiler, 
                                  _e_comp_wl_comp_cb_region_destroy);
}

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

static void 
_e_comp_wl_subcomp_cb_subsurface_get(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t id EINA_UNUSED, struct wl_resource *surface_resource EINA_UNUSED, struct wl_resource *parent_resource EINA_UNUSED)
{
   /* NB: Needs New Resource */
#warning TODO Need to subcomp subsurface
}

static const struct wl_compositor_interface _e_comp_interface = 
{
   _e_comp_wl_comp_cb_surface_create,
   _e_comp_wl_comp_cb_region_create
};

static const struct wl_subcompositor_interface _e_subcomp_interface = 
{
   _e_comp_wl_subcomp_cb_destroy,
   _e_comp_wl_subcomp_cb_subsurface_get
};

static void 
_e_comp_wl_cb_bind_compositor(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
   E_Comp *comp;
   struct wl_resource *res;

   if (!(comp = data)) return;

   res = 
     wl_resource_create(client, &wl_compositor_interface, MIN(version, 3), id);
   if (!res)
     {
        wl_client_post_no_memory(client);
        return;
     }

   wl_resource_set_implementation(res, &_e_comp_interface, comp, NULL);
}

static void 
_e_comp_wl_cb_bind_subcompositor(struct wl_client *client, void *data, uint32_t version, uint32_t id)
{
   E_Comp *comp;
   struct wl_resource *res;

   if (!(comp = data)) return;

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

   wl_resource_set_implementation(res, &_e_subcomp_interface, comp, NULL);
}

static void 
_e_comp_wl_cb_render_post(void *data EINA_UNUSED, Evas *evas EINA_UNUSED, void *event EINA_UNUSED)
{
   Eina_Iterator *itr;
   E_Client *ec;

   if (!(itr = eina_hash_iterator_data_new(clients_win_hash))) return;

   EINA_ITERATOR_FOREACH(itr, ec)
     {
        struct wl_resource *cb;

        if (!ec->wl_comp_data) continue;
        EINA_LIST_FREE(ec->wl_comp_data->frames, cb)
          {
             wl_callback_send_done(cb, ecore_loop_time_get());
             wl_resource_destroy(cb);
          }

        /* post a buffer release */
        /* TODO: FIXME: We need a way to determine if the client wants to 
         * keep the buffer or not. If so, then we should Not be setting NULL 
         * here as this will essentially release the buffer */
        _e_comp_wl_buffer_reference(&ec->wl_comp_data->buffer_ref, NULL);
     }

   eina_iterator_free(itr);
}

static void 
_e_comp_wl_cb_del(E_Comp *comp)
{
   E_Comp_Wl_Data *cdata;

   cdata = comp->wl_comp_data;

   e_comp_wl_data_manager_shutdown(cdata);
   e_comp_wl_input_shutdown(cdata);

   /* remove render_post callback */
   evas_event_callback_del_full(comp->evas, EVAS_CALLBACK_RENDER_POST, 
                                _e_comp_wl_cb_render_post, NULL);

   /* delete idler to flush clients */
   if (cdata->idler) ecore_idler_del(cdata->idler);

   /* delete fd handler to listen for wayland main loop events */
   if (cdata->fd_hdlr) ecore_main_fd_handler_del(cdata->fd_hdlr);

   /* delete the wayland display */
   if (cdata->wl.disp) wl_display_destroy(cdata->wl.disp);

   free(cdata);
}

static Eina_Bool 
_e_comp_wl_cb_read(void *data, Ecore_Fd_Handler *hdl EINA_UNUSED)
{
   E_Comp_Wl_Data *cdata;

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;
   if (!cdata->wl.disp) return ECORE_CALLBACK_RENEW;

   /* flush any pending client events */
   wl_display_flush_clients(cdata->wl.disp);

   /* dispatch any pending main loop events */
   wl_event_loop_dispatch(cdata->wl.loop, 0);

   return ECORE_CALLBACK_RENEW;
}

static Eina_Bool 
_e_comp_wl_cb_idle(void *data)
{
   E_Comp_Wl_Data *cdata;

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;
   if (!cdata->wl.disp) return ECORE_CALLBACK_RENEW;

   /* flush any pending client events */
   wl_display_flush_clients(cdata->wl.disp);

   /* dispatch any pending main loop events */
   wl_event_loop_dispatch(cdata->wl.loop, 0);

   return ECORE_CALLBACK_RENEW;
}

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

   if (!(cdata = data)) return ECORE_CALLBACK_RENEW;

   if (e_module_loading_get()) return ECORE_CALLBACK_RENEW;

   /* FIXME: make which shell to load configurable */
   if (!(mod = e_module_find("wl_desktop_shell")))
     mod = e_module_new("wl_desktop_shell");

   if (mod)
     {
        e_module_enable(mod);

        /* dispatch any pending main loop events */
        wl_event_loop_dispatch(cdata->wl.loop, 0);

        return ECORE_CALLBACK_CANCEL;
     }

   return ECORE_CALLBACK_RENEW;
}

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

   if (!(ec = data)) return EINA_TRUE;
   ec->wl_comp_data->first_draw_tmr = NULL;
   e_comp_object_damage(ec->frame, 0, 0, ec->w, ec->h);
   return EINA_FALSE;
}

static Eina_Bool 
_e_comp_wl_compositor_create(void)
{
   E_Comp *comp;
   E_Comp_Wl_Data *cdata;
   char buff[PATH_MAX];
   /* char *rules, *model, *layout; */
   int fd = 0;

   /* get the current compositor */
   if (!(comp = e_comp_get(NULL)))
     {
        comp = e_comp_new();
        comp->comp_type = E_PIXMAP_TYPE_WL;
        E_OBJECT_DEL_SET(comp, _e_comp_wl_cb_del);
     }

   /* check compositor type and make sure it's Wayland */
   /* if (comp->comp_type != E_PIXMAP_TYPE_WL) return EINA_FALSE; */

   cdata = E_NEW(E_Comp_Wl_Data, 1);
   comp->wl_comp_data = cdata;

   /* setup wayland display environment variable */
   snprintf(buff, sizeof(buff), "%s/wayland-0", e_ipc_socket);
   e_env_set("WAYLAND_DISPLAY", buff);

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

   if (wl_display_add_socket(cdata->wl.disp, buff))
     {
        ERR("Could not create a Wayland Display: %m");
        goto disp_err;
     }

   /* setup wayland compositor signals */
   /* NB: So far, we don't need ANY of these... */
   /* wl_signal_init(&cdata->signals.destroy); */
   /* wl_signal_init(&cdata->signals.activate); */
   /* wl_signal_init(&cdata->signals.transform); */
   /* wl_signal_init(&cdata->signals.kill); */
   /* wl_signal_init(&cdata->signals.idle); */
   /* wl_signal_init(&cdata->signals.wake); */
   /* wl_signal_init(&cdata->signals.session); */
   /* wl_signal_init(&cdata->signals.seat.created); */
   /* wl_signal_init(&cdata->signals.seat.destroyed); */
   /* wl_signal_init(&cdata->signals.seat.moved); */
   /* wl_signal_init(&cdata->signals.output.created); */
   /* wl_signal_init(&cdata->signals.output.destroyed); */
   /* wl_signal_init(&cdata->signals.output.moved); */

   /* try to add compositor to wayland display globals */
   if (!wl_global_create(cdata->wl.disp, &wl_compositor_interface, 3, 
                         comp, _e_comp_wl_cb_bind_compositor))
     {
        ERR("Could not add compositor to globals: %m");
        goto disp_err;
     }

   /* try to add subcompositor to wayland display globals */
   if (!wl_global_create(cdata->wl.disp, &wl_subcompositor_interface, 1, 
                         comp, _e_comp_wl_cb_bind_subcompositor))
     {
        ERR("Could not add compositor to globals: %m");
        goto disp_err;
     }

   /* try to init data manager */
   if (!e_comp_wl_data_manager_init(cdata))
     {
        ERR("Could not initialize data manager");
        goto disp_err;
     }

   /* try to init input (keyboard & pointer) */
   if (!e_comp_wl_input_init(cdata))
     {
        ERR("Could not initialize input");
        goto disp_err;
     }

/* #ifndef HAVE_WAYLAND_ONLY */
/*    if (getenv("DISPLAY")) */
/*      { */
/*         E_Config_XKB_Layout *ekbd; */
/*         Ecore_X_Atom xkb = 0; */
/*         Ecore_X_Window root = 0; */
/*         int len = 0; */
/*         unsigned char *dat; */

/*         if ((ekbd = e_xkb_layout_get())) */
/*           { */
/*              model = strdup(ekbd->model); */
/*              layout = strdup(ekbd->name); */
/*           } */

/*         root = ecore_x_window_root_first_get(); */
/*         xkb = ecore_x_atom_get("_XKB_RULES_NAMES"); */
/*         ecore_x_window_prop_property_get(root, xkb, ECORE_X_ATOM_STRING,  */
/*                                          1024, &dat, &len); */
/*         if ((dat) && (len > 0)) */
/*           { */
/*              rules = (char *)dat; */
/*              dat += strlen((const char *)dat) + 1; */
/*              if (!model) model = strdup((const char *)dat); */
/*              dat += strlen((const char *)dat) + 1; */
/*              if (!layout) layout = strdup((const char *)dat); */
/*           } */
/*      } */
/* #endif */

   /* fallback */
   /* if (!rules) rules = strdup("evdev"); */
   /* if (!model) model = strdup("pc105"); */
   /* if (!layout) layout = strdup("us"); */

   /* update compositor keymap */
   /* e_comp_wl_input_keymap_set(cdata, rules, model, layout); */

   /* TODO: init text backend */

   /* initialize shm mechanism */
   wl_display_init_shm(cdata->wl.disp);

   /* check for gl rendering */
   if ((e_comp_gl_get()) && 
       (e_comp_config_get()->engine == E_COMP_ENGINE_GL))
     {
        /* TODO: setup gl ? */
     }

   /* get the wayland display's event loop */
   cdata->wl.loop= wl_display_get_event_loop(cdata->wl.disp);

   /* get the file descriptor of the main loop */
   fd = wl_event_loop_get_fd(cdata->wl.loop);

   /* add an fd handler to listen for wayland main loop events */
   cdata->fd_hdlr = 
     ecore_main_fd_handler_add(fd, ECORE_FD_READ | ECORE_FD_WRITE, 
                               _e_comp_wl_cb_read, cdata, NULL, NULL);

   /* add an idler to flush clients */
   cdata->idler = ecore_idle_enterer_add(_e_comp_wl_cb_idle, cdata);

   /* setup module idler to load shell module */
   ecore_idler_add(_e_comp_wl_cb_module_idle, cdata);

   /* add a render post callback so we can send frame_done to the surface */
   evas_event_callback_add(comp->evas, EVAS_CALLBACK_RENDER_POST, 
                           _e_comp_wl_cb_render_post, NULL);

   return EINA_TRUE;

disp_err:
   e_env_unset("WAYLAND_DISPLAY");
   return EINA_FALSE;
}

static Eina_Bool 
_e_comp_wl_client_idler(void *data EINA_UNUSED)
{
   E_Client *ec;
   E_Comp *comp;
   const Eina_List *l;

   EINA_LIST_FREE(_idle_clients, ec)
     {
        if ((e_object_is_del(E_OBJECT(ec))) || (!ec->wl_comp_data)) continue;

        ec->post_move = 0;
        ec->post_resize = 0;
     }

   EINA_LIST_FOREACH(e_comp_list(), l, comp)
     {
        if ((comp->wl_comp_data->restack) && (!comp->new_clients))
          {
             e_hints_client_stacking_set();
             comp->wl_comp_data->restack = EINA_FALSE;
          }
     }

   _client_idler = NULL;
   return EINA_FALSE;
}

static void 
_e_comp_wl_client_idler_add(E_Client *ec)
{
   if (!_client_idler) 
     _client_idler = ecore_idle_enterer_add(_e_comp_wl_client_idler, NULL);

   if (!ec) CRI("ACK!");

   if (!eina_list_data_find(_idle_clients, ec))
     _idle_clients = eina_list_append(_idle_clients, ec);
}

static void 
_e_comp_wl_client_priority_adjust(int pid, int set, int adj, Eina_Bool use_adj, Eina_Bool adj_child, Eina_Bool do_child)
{
   int n;

   n = set;
   if (use_adj) n = (getpriority(PRIO_PROCESS, pid) + adj);
   setpriority(PRIO_PROCESS, pid, n);

   if (do_child)
     {
        Eina_List *files;
        char *file, buff[PATH_MAX];
        FILE *f;
        int pid2, ppid;

        files = ecore_file_ls("/proc");
        EINA_LIST_FREE(files, file)
          {
             if (isdigit(file[0]))
               {
                  snprintf(buff, sizeof(buff), "/proc/%s/stat", file);
                  if ((f = fopen(buff, "r")))
                    {
                       pid2 = -1;
                       ppid = -1;
                       if (fscanf(f, "%i %*s %*s %i %*s", &pid2, &ppid) == 2)
                         {
                            fclose(f);
                            if (ppid == pid)
                              {
                                 if (adj_child)
                                   _e_comp_wl_client_priority_adjust(pid2, set, 
                                                                     adj, EINA_TRUE,
                                                                     adj_child, do_child);
                                 else
                                   _e_comp_wl_client_priority_adjust(pid2, set, 
                                                                     adj, use_adj,
                                                                     adj_child, do_child);
                              }
                         }
                       else 
                         fclose(f);
                    }
               }
             free(file);
          }
     }
}

static void 
_e_comp_wl_client_priority_raise(E_Client *ec)
{
   if (ec->netwm.pid <= 0) return;
   if (ec->netwm.pid == getpid()) return;
   _e_comp_wl_client_priority_adjust(ec->netwm.pid, 
                                     e_config->priority - 1, -1, 
                                     EINA_FALSE, EINA_TRUE, EINA_FALSE);
}

static void 
_e_comp_wl_client_priority_normal(E_Client *ec)
{
   if (ec->netwm.pid <= 0) return;
   if (ec->netwm.pid == getpid()) return;
   _e_comp_wl_client_priority_adjust(ec->netwm.pid, e_config->priority, 1, 
                                     EINA_FALSE, EINA_TRUE, EINA_FALSE);
}

static void 
_e_comp_wl_evas_cb_show(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *tmp;
   Eina_List *l;

   if (!(ec = data)) return;

   if (!ec->override) 
     e_hints_window_visible_set(ec);

   if (ec->wl_comp_data->frame_update)
     ec->wl_comp_data->frame_update = EINA_FALSE;

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

static void 
_e_comp_wl_evas_cb_hide(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *tmp;
   Eina_List *l;

   if (!(ec = data)) return;

   EINA_LIST_FOREACH(ec->e.state.video_child, l, tmp)
     evas_object_hide(tmp->frame);
}

static void 
_e_comp_wl_evas_cb_mouse_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_In *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial;

   ev = event;
   if (!(ec = data)) return;
   if (!ec->wl_comp_data) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_enter(res, serial, ec->wl_comp_data->surface, 
                              wl_fixed_from_int(ev->canvas.x), 
                              wl_fixed_from_int(ev->canvas.y));
     }
}

static void 
_e_comp_wl_evas_cb_mouse_out(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial;

   if (!(ec = data)) return;
   if (!ec->wl_comp_data) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_leave(res, serial, ec->wl_comp_data->surface);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Down *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, btn;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   switch (ev->button)
     {
      case 1:
        btn = BTN_LEFT;
        break;
      case 2:
        btn = BTN_MIDDLE;
        break;
      case 3:
        btn = BTN_RIGHT;
        break;
      default:
        btn = ev->button;
        break;
     }

   ec->comp->wl_comp_data->ptr.button = btn;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_button(res, serial, ev->timestamp, btn, 
                               WL_POINTER_BUTTON_STATE_PRESSED);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Up *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, btn;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   switch (ev->button)
     {
      case 1:
        btn = BTN_LEFT;
        break;
      case 2:
        btn = BTN_MIDDLE;
        break;
      case 3:
        btn = BTN_RIGHT;
        break;
      default:
        btn = ev->button;
        break;
     }

   ec->comp->wl_comp_data->resize.resource = NULL;
   ec->comp->wl_comp_data->ptr.button = btn;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_button(res, serial, ev->timestamp, btn, 
                               WL_POINTER_BUTTON_STATE_RELEASED);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_wheel(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Wheel *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t axis, dir;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   if (ev->direction == 0)
     axis = WL_POINTER_AXIS_VERTICAL_SCROLL;
   else
     axis = WL_POINTER_AXIS_HORIZONTAL_SCROLL;

   if (ev->z < 0)
     dir = -wl_fixed_from_int(abs(ev->z));
   else
     dir = wl_fixed_from_int(ev->z);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_axis(res, ev->timestamp, axis, dir);
     }
}

static void 
_e_comp_wl_evas_cb_mouse_move(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   Evas_Event_Mouse_Move *ev;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (ec->cur_mouse_action) return;
   if (e_object_is_del(E_OBJECT(ec))) return;
   if (e_client_util_ignored_get(ec)) return;

   ec->comp->wl_comp_data->ptr.x = 
     wl_fixed_from_int(ev->cur.canvas.x - ec->client.x);
   ec->comp->wl_comp_data->ptr.y = 
     wl_fixed_from_int(ev->cur.canvas.y - ec->client.y);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->ptr.resources, l, res)
     {
        if (!e_comp_wl_input_pointer_check(res)) continue;
        if (wl_resource_get_client(res) != wc) continue;
        wl_pointer_send_motion(res, ev->timestamp, 
                               ec->comp->wl_comp_data->ptr.x, 
                               ec->comp->wl_comp_data->ptr.y);
     }
}

static void 
_e_comp_wl_evas_cb_key_down(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   E_Comp_Wl_Data *cdata;
   Evas_Event_Key_Down *ev;
   uint32_t serial, *end, *k, keycode;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   if (!ec->focused) return;

   keycode = (ev->keycode - 8);
   if (!(cdata = ec->comp->wl_comp_data)) return;

   end = (uint32_t *)cdata->kbd.keys.data + (cdata->kbd.keys.size / sizeof(*k));

   for (k = cdata->kbd.keys.data; k < end; k++)
     {
        /* ignore server-generated key repeats */
        if (*k == keycode) return;
     }

   cdata->kbd.keys.size = (const char *)end - (const char *)cdata->kbd.keys.data;
   k = wl_array_add(&cdata->kbd.keys, sizeof(*k));
   *k = keycode;

   /* update modifier state */
   e_comp_wl_input_keyboard_state_update(cdata, keycode, EINA_TRUE);

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(ec->comp->wl_comp_data->wl.disp);
   EINA_LIST_FOREACH(ec->comp->wl_comp_data->kbd.resources, l, res)
     {
        if (wl_resource_get_client(res) != wc) continue;
        wl_keyboard_send_key(res, serial, ev->timestamp, 
                             keycode, WL_KEYBOARD_KEY_STATE_PRESSED);
     }
}

static void 
_e_comp_wl_evas_cb_key_up(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event)
{
   E_Client *ec;
   E_Comp_Wl_Data *cdata;
   Evas_Event_Key_Up *ev;
   uint32_t serial, *end, *k, keycode;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;

   ev = event;
   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   if (!ec->focused) return;

   keycode = (ev->keycode - 8);
   if (!(cdata = ec->comp->wl_comp_data)) return;

   end = (uint32_t *)cdata->kbd.keys.data + (cdata->kbd.keys.size / sizeof(*k));
   for (k = cdata->kbd.keys.data; k < end; k++)
     if (*k == keycode) *k = *--end;

   cdata->kbd.keys.size = (const char *)end - (const char *)cdata->kbd.keys.data;

   wc = wl_resource_get_client(ec->wl_comp_data->surface);
   serial = wl_display_next_serial(cdata->wl.disp);
   EINA_LIST_FOREACH(cdata->kbd.resources, l, res)
     {
        if (wl_resource_get_client(res) != wc) continue;
        wl_keyboard_send_key(res, serial, ev->timestamp, 
                             keycode, WL_KEYBOARD_KEY_STATE_RELEASED);
     }

   /* update modifier state */
   e_comp_wl_input_keyboard_state_update(cdata, keycode, EINA_FALSE);
}

static void 
_e_comp_wl_evas_cb_focus_in(void *data, Evas *evas EINA_UNUSED, Evas_Object *obj EINA_UNUSED, void *event EINA_UNUSED)
{
   E_Client *ec, *focused;
   E_Comp_Wl_Data *cdata;
   struct wl_resource *res;
   struct wl_client *wc;
   Eina_List *l;
   uint32_t serial, *k;

   if (!(ec = data)) return;
   if (e_object_is_del(E_OBJECT(ec))) return;

   /* block refocus attempts on iconic clients
    * these result from iconifying a client during a grab */
{'fraction_non_alphanumeric': 0.12000770128635549, 'fraction_numerical': 0.003826576657882387, 'mean_word_length': 2.804953985623369, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 823, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 66, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3667302', 'n_tokens_mistral': 31550, 'n_tokens_neox': 30561, 'n_words': 5443}