diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h index c219b647c..df70bc467 100644 --- a/src/bin/e_comp_wl.h +++ b/src/bin/e_comp_wl.h @@ -107,9 +107,18 @@ struct _E_Comp_Wl_Data struct { struct wl_global *global; + Eina_List *data_resource_list; } mgr; - struct + struct + { + void *data_source; + uint32_t serial; + struct wl_signal signal; + struct wl_listener data_source_listener; + } selection; + + struct { struct wl_resource *resource; int32_t width, height; diff --git a/src/bin/e_comp_wl_data.c b/src/bin/e_comp_wl_data.c index bfc4ce017..57ba68892 100644 --- a/src/bin/e_comp_wl_data.c +++ b/src/bin/e_comp_wl_data.c @@ -3,6 +3,186 @@ #include "e_comp_wl_data.h" static void +_e_comp_wl_data_offer_cb_accept(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t serial, const char *mime_type) +{ + E_Comp_Wl_Data_Offer *offer; + + if (!(offer = wl_resource_get_user_data(resource))) + return; + + if (offer->source) + offer->source->target(offer->source, serial, mime_type); +} + +static void +_e_comp_wl_data_offer_cb_receive(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *mime_type, int32_t fd) +{ + E_Comp_Wl_Data_Offer *offer; + + if (!(offer = wl_resource_get_user_data(resource))) + return; + + if (offer->source) + offer->source->send(offer->source, mime_type, fd); + else + close(fd); +} + +/* called by wl_data_offer_destroy */ +static void +_e_comp_wl_data_offer_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/* called by wl_resource_destroy */ +static void +_e_comp_wl_data_offer_cb_resource_destroy(struct wl_resource *resource) +{ + E_Comp_Wl_Data_Offer *offer; + + if (!(offer = wl_resource_get_user_data(resource))) + return; + + if (offer->source) + wl_list_remove(&offer->source_destroy_listener.link); + + free(offer); +} + +/* called by emission of source->destroy_signal */ +static void +_e_comp_wl_data_offer_cb_source_destroy(struct wl_listener *listener, void *data EINA_UNUSED) +{ + E_Comp_Wl_Data_Offer *offer; + + offer = container_of(listener, E_Comp_Wl_Data_Offer, source_destroy_listener); + offer->source = NULL; +} + +static const struct wl_data_offer_interface _e_data_offer_interface = +{ + _e_comp_wl_data_offer_cb_accept, + _e_comp_wl_data_offer_cb_receive, + _e_comp_wl_data_offer_cb_destroy, +}; + +static void +_e_comp_wl_data_source_cb_offer(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, const char *mime_type) +{ + E_Comp_Wl_Data_Source *source; + + if (!(source = wl_resource_get_user_data(resource))) + return; + + source->mime_types = eina_list_append(source->mime_types, eina_stringshare_add(mime_type)); +} + +/* called by wl_data_source_destroy */ +static void +_e_comp_wl_data_source_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl_resource *resource) +{ + wl_resource_destroy(resource); +} + +/* called by wl_resource_destroy */ +static void +_e_comp_wl_data_source_cb_resource_destroy(struct wl_resource *resource) +{ + E_Comp_Wl_Data_Source *source; + char *t; + + if (!(source = wl_resource_get_user_data(resource))) + return; + + wl_signal_emit(&source->destroy_signal, source); + + EINA_LIST_FREE(source->mime_types, t) + eina_stringshare_del(t); + + free(source); +} + +static void +_e_comp_wl_data_source_target_send(E_Comp_Wl_Data_Source *source, uint32_t serial EINA_UNUSED, const char* mime_type) +{ + wl_data_source_send_target(source->resource, mime_type); +} + +static void +_e_comp_wl_data_source_send_send(E_Comp_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 +_e_comp_wl_data_source_cancelled_send(E_Comp_Wl_Data_Source *source) +{ + wl_data_source_send_cancelled(source->resource); +} + +static const struct wl_data_source_interface _e_data_source_interface = +{ + _e_comp_wl_data_source_cb_offer, + _e_comp_wl_data_source_cb_destroy, +}; + +static void +_e_comp_wl_data_device_destroy_selection_data_source(struct wl_listener *listener, void *data) +{ + E_Comp_Wl_Data *cdata; + E_Comp_Wl_Data_Source *source; + struct wl_resource *data_device_res; + + if (!(source = (E_Comp_Wl_Data_Source*)data)) + return; + + if (!(cdata = container_of(listener, E_Comp_Wl_Data, selection.data_source_listener))) + return; + + cdata->selection.data_source = NULL; + + /* TODO: get data device from a focused surface */ + data_device_res = _e_comp_wl_data_find_for_client(cdata->mgr.data_resource_list, wl_resource_get_client(source->resource)); + + if (data_device_res) + wl_data_device_send_selection(data_device_res, NULL); + + wl_signal_emit(&cdata->selection.signal, cdata); +} + +static struct wl_resource* +_e_comp_wl_data_device_data_offer_create(E_Comp_Wl_Data_Source *source, struct wl_resource *data_device_res) +{ + E_Comp_Wl_Data_Offer *offer; + Eina_List *l; + char *t; + + offer = E_NEW(E_Comp_Wl_Data_Offer, 1); + if (!offer) return NULL; + + offer->resource = wl_resource_create(wl_resource_get_client(data_device_res), &wl_data_offer_interface, 1, 0); + if (!offer->resource) + { + free(offer); + return NULL; + } + + wl_resource_set_implementation(offer->resource, &_e_data_offer_interface, offer, _e_comp_wl_data_offer_cb_resource_destroy); + offer->source = source; + offer->source_destroy_listener.notify = _e_comp_wl_data_offer_cb_source_destroy; + wl_signal_add(&source->destroy_signal, &offer->source_destroy_listener); + + wl_data_device_send_data_offer(data_device_res, offer->resource); + + EINA_LIST_FOREACH(source->mime_types, l, t) + wl_data_offer_send_offer(offer->resource, t); + + return offer->resource; +} + +static void _e_comp_wl_data_device_cb_drag_start(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource EINA_UNUSED, struct wl_resource *origin_resource EINA_UNUSED, struct wl_resource *icon_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) { @@ -11,7 +191,51 @@ _e_comp_wl_data_device_cb_drag_start(struct wl_client *client EINA_UNUSED, struc static void _e_comp_wl_data_device_cb_selection_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) { + E_Comp_Wl_Data *cdata; + E_Comp_Wl_Data_Source *source, *sel_source; + struct wl_resource *offer_res, *data_device_res; + if (!source_resource) return; + if (!(cdata = wl_resource_get_user_data(resource))) return; + + source = wl_resource_get_user_data(source_resource); + sel_source = (E_Comp_Wl_Data_Source*)cdata->selection.data_source; + + if ((sel_source) && + (cdata->selection.serial - serial < UINT32_MAX / 2)) + return; + + if (sel_source) + { + if (sel_source->cancelled) + sel_source->cancelled(sel_source); + wl_list_remove(&cdata->selection.data_source_listener.link); + cdata->selection.data_source = NULL; + } + + cdata->selection.data_source = sel_source = source; + cdata->selection.serial = serial; + + /* TODO: get data device from a focused surface */ + data_device_res = _e_comp_wl_data_find_for_client(cdata->mgr.data_resource_list, wl_resource_get_client(source->resource)); + + if ((data_device_res) && (source)) + { + offer_res = _e_comp_wl_data_device_data_offer_create(source, data_device_res); + wl_data_device_send_selection(data_device_res, offer_res); + } + else if (data_device_res) + { + wl_data_device_send_selection(data_device_res, NULL); + } + + wl_signal_emit(&cdata->selection.signal, cdata); + + if (source) + { + cdata->selection.data_source_listener.notify = _e_comp_wl_data_device_destroy_selection_data_source; + wl_signal_add(&source->destroy_signal, &cdata->selection.data_source_listener); + } } static const struct wl_data_device_interface _e_data_device_interface = @@ -20,14 +244,47 @@ static const struct wl_data_device_interface _e_data_device_interface = _e_comp_wl_data_device_cb_selection_set, }; -static void -_e_comp_wl_data_manager_cb_source_create(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t id EINA_UNUSED) +static void +_e_comp_wl_data_device_cb_unbind(struct wl_resource *resource) { - /* NB: New resource */ + E_Comp_Wl_Data *cdata; + + if(!(cdata = wl_resource_get_user_data(resource))) + return; + + cdata->mgr.data_resource_list = eina_list_remove(cdata->mgr.data_resource_list, resource); +} + +static void +_e_comp_wl_data_manager_cb_source_create(struct wl_client *client EINA_UNUSED, struct wl_resource *resource, uint32_t id EINA_UNUSED) +{ + E_Comp_Wl_Data_Source *source; + + source = E_NEW(E_Comp_Wl_Data_Source, 1); + if (!source) + { + wl_resource_post_no_memory(resource); + return; + } + + wl_signal_init(&source->destroy_signal); + source->target = _e_comp_wl_data_source_target_send; + source->send = _e_comp_wl_data_source_send_send; + source->cancelled = _e_comp_wl_data_source_cancelled_send; + + source->resource = wl_resource_create(client, &wl_data_source_interface, 1, id); + if (!source->resource) + { + ERR("Could not create data source resource: %m"); + free(source); + wl_resource_post_no_memory(resource); + return; + } + wl_resource_set_implementation(source->resource, &_e_data_source_interface, source, _e_comp_wl_data_source_cb_resource_destroy); } static void -_e_comp_wl_data_manager_cb_device_get(struct wl_client *client EINA_UNUSED, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) +_e_comp_wl_data_manager_cb_device_get(struct wl_client *client, struct wl_resource *manager_resource, uint32_t id, struct wl_resource *seat_resource) { E_Comp_Wl_Data *cdata; struct wl_resource *res; @@ -46,7 +303,8 @@ _e_comp_wl_data_manager_cb_device_get(struct wl_client *client EINA_UNUSED, stru return; } - wl_resource_set_implementation(res, &_e_data_device_interface, cdata, NULL); + cdata->mgr.data_resource_list = eina_list_append(cdata->mgr.data_resource_list, res); + wl_resource_set_implementation(res, &_e_data_device_interface, cdata, _e_comp_wl_data_device_cb_unbind); } static const struct wl_data_device_manager_interface _e_manager_interface = @@ -96,15 +354,17 @@ e_comp_wl_data_manager_init(E_Comp_Wl_Data *cdata) if (!cdata) return EINA_FALSE; /* try to create global data manager */ - cdata->mgr.global = - wl_global_create(cdata->wl.disp, &wl_data_device_manager_interface, 1, + cdata->mgr.global = + wl_global_create(cdata->wl.disp, &wl_data_device_manager_interface, 1, cdata, _e_comp_wl_data_cb_bind_manager); - if (!cdata->mgr.global) + if (!cdata->mgr.global) { ERR("Could not create global for data device manager: %m"); return EINA_FALSE; } + wl_signal_init(&cdata->selection.signal); + return EINA_TRUE; } diff --git a/src/bin/e_comp_wl_data.h b/src/bin/e_comp_wl_data.h index 70c445f4d..23d236236 100644 --- a/src/bin/e_comp_wl_data.h +++ b/src/bin/e_comp_wl_data.h @@ -3,6 +3,29 @@ # ifndef E_COMP_WL_DATA_H # define E_COMP_WL_DATA_H +typedef struct _E_Comp_Wl_Data_Source E_Comp_Wl_Data_Source; +typedef struct _E_Comp_Wl_Data_Offer E_Comp_Wl_Data_Offer; + +struct _E_Comp_Wl_Data_Source +{ + struct wl_resource *resource; //resource of wl_data_source + + Eina_List *mime_types; //mime_type list to offer from source + struct wl_signal destroy_signal; //signal to emit when wl_data_source resource is destroyed + + void (*target) (E_Comp_Wl_Data_Source *source, uint32_t serial, const char* mime_type); + void (*send) (E_Comp_Wl_Data_Source *source, const char* mime_type, int32_t fd); + void (*cancelled) (E_Comp_Wl_Data_Source *source); +}; + +struct _E_Comp_Wl_Data_Offer +{ + struct wl_resource *resource; //resource of wl_data_offer + + E_Comp_Wl_Data_Source *source; //indicates source client data + struct wl_listener source_destroy_listener; //listener for destroy of source +}; + EINTERN Eina_Bool e_comp_wl_data_manager_init(E_Comp_Wl_Data *cdata); EINTERN void e_comp_wl_data_manager_shutdown(E_Comp_Wl_Data *cdata);