From bfd204be715904313b3bffedb63bcae0a30b4aa9 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Tue, 19 Apr 2016 17:34:58 -0400 Subject: [PATCH] update wl dnd/clipboard support to v3 of protocol this code is mostly copied from weston: 78d4bf9a3ec990dceee23fd53962a69891352a0e 9c93179023fe894e417ccd20533d72d672d976fc b288988e831cee3deb7f8bb1a3f440c86230dd9f 4061e2b67e62d5d2a635f0b87098f331082e8145 credit to Carlos Garnacho as original author ref T3455 --- src/bin/e_comp_wl_data.c | 323 +++++++++++++++++++++++++++++++++++---- src/bin/e_comp_wl_data.h | 11 ++ 2 files changed, 303 insertions(+), 31 deletions(-) diff --git a/src/bin/e_comp_wl_data.c b/src/bin/e_comp_wl_data.c index 5ac560924..49bdfa3b2 100644 --- a/src/bin/e_comp_wl_data.c +++ b/src/bin/e_comp_wl_data.c @@ -27,6 +27,18 @@ #define E_COMP_WL #include "e.h" +#if __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wunused-parameter" +#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wunused-parameter" +#endif + +#define ALL_ACTIONS (WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE | \ + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK) + static void _mime_types_free(E_Comp_Wl_Data_Source *source) { @@ -45,8 +57,12 @@ _e_comp_wl_data_offer_cb_accept(struct wl_client *client EINA_UNUSED, struct wl_ if (!(offer = wl_resource_get_user_data(resource))) return; - if (offer->source) - offer->source->target(offer->source, serial, mime_type); + /* Protect against untimely calls from older data offers */ + if ((!offer->source) || (offer != offer->source->offer)) + return; + + offer->source->target(offer->source, serial, mime_type); + offer->source->accepted = !!mime_type; } static void @@ -74,30 +90,181 @@ _e_comp_wl_data_offer_cb_destroy(struct wl_client *client EINA_UNUSED, struct wl } static void -_e_comp_wl_data_offer_cb_finish(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED) +data_source_notify_finish(E_Comp_Wl_Data_Source *source) { - /* TODO: implement */ + if (!source->actions_set) + return; + + if (source->offer->in_ask && + wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) { + wl_data_source_send_action(source->resource, + source->current_dnd_action); + } + + if (wl_resource_get_version(source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_dnd_finished(source->resource); + } + + source->offer = NULL; +} + +static uint32_t +data_offer_choose_action(E_Comp_Wl_Data_Offer *offer) +{ + uint32_t available_actions, preferred_action = 0; + uint32_t source_actions, offer_actions; + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + offer_actions = offer->dnd_actions; + preferred_action = offer->preferred_dnd_action; + } else { + offer_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + } + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) + source_actions = offer->source->dnd_actions; + else + source_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + + available_actions = offer_actions & source_actions; + + if (!available_actions) + return WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE; + + if (//offer->source->seat && + offer->source->compositor_action & available_actions) + return offer->source->compositor_action; + + /* If the dest side has a preferred DnD action, use it */ + if ((preferred_action & available_actions) != 0) + return preferred_action; + + /* Use the first found action, in bit order */ + return 1 << (ffs(available_actions) - 1); } static void -_e_comp_wl_data_offer_cb_actions_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t actions EINA_UNUSED, uint32_t preferred_action EINA_UNUSED) +data_offer_update_action(E_Comp_Wl_Data_Offer *offer) { - /* TODO: implement */ + uint32_t action; + + if (!offer->source || !offer->source->actions_set) + return; + + action = data_offer_choose_action(offer); + + if (offer->source->current_dnd_action == action) + return; + + offer->source->current_dnd_action = action; + + if (offer->in_ask) + return; + + if (wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_ACTION_SINCE_VERSION) + wl_data_source_send_action(offer->source->resource, action); + + if (wl_resource_get_version(offer->resource) >= + WL_DATA_OFFER_ACTION_SINCE_VERSION) + wl_data_offer_send_action(offer->resource, action); +} + +static void +data_offer_set_actions(struct wl_client *client, + struct wl_resource *resource, + uint32_t dnd_actions, uint32_t preferred_action) +{ + E_Comp_Wl_Data_Offer *offer = wl_resource_get_user_data(resource); + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } + + if (preferred_action && + (!(preferred_action & dnd_actions) || + __builtin_popcount(preferred_action) > 1)) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_ACTION, + "invalid action %x", preferred_action); + return; + } + + offer->dnd_actions = dnd_actions; + offer->preferred_dnd_action = preferred_action; + data_offer_update_action(offer); +} + +static void +data_offer_finish(struct wl_client *client, struct wl_resource *resource) +{ + E_Comp_Wl_Data_Offer *offer = wl_resource_get_user_data(resource); + + if (!offer->source || offer->source->offer != offer) + return; + + /* Disallow finish while we have a grab driving drag-and-drop, or + * if the negotiation is not at the right stage + */ + if (//offer->source->seat || + !offer->source->accepted) { + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_FINISH, + "premature finish request"); + return; + } + + switch (offer->source->current_dnd_action) { + case WL_DATA_DEVICE_MANAGER_DND_ACTION_NONE: + case WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK: + wl_resource_post_error(offer->resource, + WL_DATA_OFFER_ERROR_INVALID_OFFER, + "offer finished with an invalid action"); + return; + default: + break; + } + + data_source_notify_finish(offer->source); } /* 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; + E_Comp_Wl_Data_Offer *offer = wl_resource_get_user_data(resource); - if (!(offer = wl_resource_get_user_data(resource))) - return; + if (!offer->source) + goto out; - if (offer->source) - wl_list_remove(&offer->source_destroy_listener.link); + wl_list_remove(&offer->source_destroy_listener.link); - free(offer); + if (offer->source->offer != offer) + goto out; + + /* If the drag destination has version < 3, wl_data_offer.finish + * won't be called, so do this here as a safety net, because + * we still want the version >=3 drag source to be happy. + */ + if (wl_resource_get_version(offer->resource) < + WL_DATA_OFFER_ACTION_SINCE_VERSION) { + data_source_notify_finish(offer->source); + } else if (offer->source->resource && + wl_resource_get_version(offer->source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) { + wl_data_source_send_cancelled(offer->source->resource); + } + + offer->source->offer = NULL; +out: + free(offer); } /* called by emission of source->destroy_signal */ @@ -119,8 +286,8 @@ 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, - _e_comp_wl_data_offer_cb_finish, - _e_comp_wl_data_offer_cb_actions_set, + data_offer_finish, + data_offer_set_actions, }; static void @@ -146,9 +313,37 @@ _e_comp_wl_data_source_cb_destroy(struct wl_client *client EINA_UNUSED, struct w } static void -_e_comp_wl_data_source_cb_actions_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, uint32_t actions EINA_UNUSED) +data_source_set_actions(struct wl_client *client, + struct wl_resource *resource, + uint32_t dnd_actions) { - /* TODO: implement */ + E_Comp_Wl_Data_Source *source = + wl_resource_get_user_data(resource); + + if (source->actions_set) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "cannot set actions more than once"); + return; + } + + if (dnd_actions & ~ALL_ACTIONS) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action mask %x", dnd_actions); + return; + } +/* FIXME + if (source->seat) { + wl_resource_post_error(source->resource, + WL_DATA_SOURCE_ERROR_INVALID_ACTION_MASK, + "invalid action change after " + "wl_data_device.start_drag"); + return; + } +*/ + source->dnd_actions = dnd_actions; + source->actions_set = 1; } /* called by wl_resource_destroy */ @@ -192,7 +387,7 @@ 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, - _e_comp_wl_data_source_cb_actions_set, + data_source_set_actions, }; static void @@ -224,7 +419,7 @@ _e_comp_wl_data_device_destroy_selection_data_source(struct wl_listener *listene } 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_device_data_offer_create(E_Comp_Wl_Data_Source *source, struct wl_resource *data_device) { E_Comp_Wl_Data_Offer *offer; Eina_Iterator *it; @@ -236,8 +431,8 @@ _e_comp_wl_data_device_data_offer_create(E_Comp_Wl_Data_Source *source, struct w if (!offer) return NULL; offer->resource = - wl_resource_create(wl_resource_get_client(data_device_res), - &wl_data_offer_interface, 1, 0); + wl_resource_create(wl_resource_get_client(data_device), + &wl_data_offer_interface, wl_resource_get_version(data_device), 0); if (!offer->resource) { free(offer); @@ -248,17 +443,20 @@ _e_comp_wl_data_device_data_offer_create(E_Comp_Wl_Data_Source *source, struct w &_e_data_offer_interface, offer, _e_comp_wl_data_offer_cb_resource_destroy); offer->source = source; + source->offer = offer; 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); + wl_data_device_send_data_offer(data_device, offer->resource); it = eina_array_iterator_new(source->mime_types); EINA_ITERATOR_FOREACH(it, t) wl_data_offer_send_offer(offer->resource, t); eina_iterator_free(it); + data_offer_update_action(offer); + return offer->resource; } @@ -270,7 +468,7 @@ _e_comp_wl_data_device_selection_set(void *data EINA_UNUSED, E_Comp_Wl_Data_Sour sel_source = (E_Comp_Wl_Data_Source*)e_comp_wl->selection.data_source; - if (sel_source) + if (sel_source && ((sel_source->serial != serial) || (sel_source != source))) { if (sel_source->cancelled) sel_source->cancelled(sel_source); @@ -281,7 +479,7 @@ _e_comp_wl_data_device_selection_set(void *data EINA_UNUSED, E_Comp_Wl_Data_Sour e_comp_wl->selection.data_source = sel_source = source; e_comp_wl->clipboard.xwl_owner = NULL; - e_comp_wl->selection.serial = serial; + source->serial = e_comp_wl->selection.serial = serial; if (e_comp_wl->kbd.enabled) focus = e_comp_wl->kbd.focus; @@ -315,13 +513,17 @@ _e_comp_wl_data_device_selection_set(void *data EINA_UNUSED, E_Comp_Wl_Data_Sour static void _e_comp_wl_data_device_drag_finished(E_Drag *drag, int dropped) { - Evas_Object *o; + Evas_Object *o, *z; o = edje_object_part_swallow_get(drag->comp_object, "e.swallow.content"); if (eina_streq(evas_object_type_get(o), "e_comp_object")) edje_object_part_unswallow(drag->comp_object, o); else - e_zoomap_child_set(o, NULL); + { + z = o; + o = e_zoomap_child_get(z); + e_zoomap_child_set(z, NULL); + } evas_object_hide(o); evas_object_pass_events_set(o, 1); if (e_comp_wl->drag != drag) return; @@ -343,11 +545,24 @@ _e_comp_wl_data_device_drag_finished(E_Drag *drag, int dropped) #endif { struct wl_resource *res; + E_Comp_Wl_Data_Source *data_source = e_comp_wl->drag_source; res = e_comp_wl_data_find_for_client(wl_resource_get_client(e_comp_wl->selection.target->comp_data->surface)); if (res) { - wl_data_device_send_drop(res); + if (data_source->accepted && data_source->current_dnd_action) + { + wl_data_device_send_drop(res); + if (wl_resource_get_version(data_source->resource) >= + WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) + wl_data_source_send_dnd_drop_performed(data_source->resource); + + data_source->offer->in_ask = data_source->current_dnd_action == + WL_DATA_DEVICE_MANAGER_DND_ACTION_ASK; + } + else if (wl_resource_get_version(data_source->resource) >= + WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) + wl_data_source_send_cancelled(data_source->resource); wl_data_device_send_leave(res); } #ifndef HAVE_WAYLAND_ONLY @@ -364,6 +579,24 @@ _e_comp_wl_data_device_drag_finished(E_Drag *drag, int dropped) } } +static void +_e_comp_wl_data_device_drag_key(E_Drag *drag EINA_UNUSED, Ecore_Event_Key *ev) +{ + uint32_t compositor_action = 0; + const Evas_Modifier *m; + E_Comp_Wl_Data_Source *drag_source = e_comp_wl->drag_source; + + m = evas_key_modifier_get(e_comp->evas); + if (evas_key_modifier_is_set(m, "Shift")) + compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE; + else if (evas_key_modifier_is_set(m, "Control")) + compositor_action = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY; + if (drag_source->compositor_action == compositor_action) return; + drag_source->compositor_action = compositor_action; + if (drag_source->offer) + data_offer_update_action(drag_source->offer); +} + static void _e_comp_wl_data_device_cb_drag_start(struct wl_client *client, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource, struct wl_resource *origin_resource, struct wl_resource *icon_resource, uint32_t serial) { @@ -409,6 +642,8 @@ _e_comp_wl_data_device_cb_drag_start(struct wl_client *client, struct wl_resourc evas_pointer_canvas_xy_get(e_comp->evas, &x, &y); e_comp_wl->drag = e_drag_new(x, y, NULL, 0, NULL, 0, NULL, _e_comp_wl_data_device_drag_finished); + e_drag_key_down_cb_set(e_comp_wl->drag, _e_comp_wl_data_device_drag_key); + e_drag_key_up_cb_set(e_comp_wl->drag, _e_comp_wl_data_device_drag_key); e_comp_wl->drag->button_mask = evas_pointer_button_down_mask_get(e_comp->evas); if (ec) @@ -437,6 +672,12 @@ _e_comp_wl_data_device_cb_selection_set(struct wl_client *client EINA_UNUSED, st DBG("Data Device Selection Set"); if (!source_resource) return; if (!(source = wl_resource_get_user_data(source_resource))) return; + if (source->actions_set) { + wl_resource_post_error(source_resource, + WL_DATA_SOURCE_ERROR_INVALID_SOURCE, + "cannot set drag-and-drop source as selection"); + return; + } _e_comp_wl_data_device_selection_set(e_comp->wl_comp_data, source, serial); } @@ -471,7 +712,7 @@ _e_comp_wl_data_manager_cb_device_get(struct wl_client *client, struct wl_resour /* try to create the data device resource */ - res = wl_resource_create(client, &wl_data_device_interface, 1, id); + res = wl_resource_create(client, &wl_data_device_interface, wl_resource_get_version(manager_resource), id); if (!res) { ERR("Could not create data device resource"); @@ -510,7 +751,7 @@ _e_comp_wl_data_cb_bind_manager(struct wl_client *client, void *data EINA_UNUSED /* try to create data manager resource */ e_comp_wl->mgr.resource = res = - wl_resource_create(client, &wl_data_device_manager_interface, 1, id); + wl_resource_create(client, &wl_data_device_manager_interface, 3, id); if (!res) { ERR("Could not create data device manager"); @@ -702,13 +943,32 @@ e_comp_wl_data_device_send_enter(E_Client *ec) if (e_client_has_xwindow(ec) && e_client_has_xwindow(e_comp_wl->drag_client)) return; + if (e_comp_wl->drag && (e_comp_wl->drag->object == ec->frame)) return; if (!e_client_has_xwindow(ec)) { + E_Comp_Wl_Data_Source *drag_source = e_comp_wl->drag_source; + if (drag_source && + drag_source->offer) + { + E_Comp_Wl_Data_Offer *offer; + /* Unlink the offer from the source */ + offer = drag_source->offer; + offer->source = NULL; + drag_source->offer = NULL; + drag_source->accepted = 0; + wl_list_remove(&offer->source_destroy_listener.link); + } data_device_res = e_comp_wl_data_find_for_client(wl_resource_get_client(ec->comp_data->surface)); if (!data_device_res) return; offer_res = e_comp_wl_data_device_send_offer(ec); if (e_comp_wl->drag_source && (!offer_res)) return; + if (offer_res) + { + if (wl_resource_get_version(offer_res) >= WL_DATA_OFFER_SOURCE_ACTIONS_SINCE_VERSION) + wl_data_offer_send_source_actions (offer_res, drag_source->dnd_actions); + } + } e_comp_wl->selection.target = ec; evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_DEL, @@ -770,6 +1030,7 @@ e_comp_wl_data_device_send_leave(E_Client *ec) if (e_client_has_xwindow(ec) && e_client_has_xwindow(e_comp_wl->drag_client)) return; + if (e_comp_wl->drag && (e_comp_wl->drag->object == ec->frame)) return; evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_DEL, _e_comp_wl_data_device_target_del, ec); if (e_comp_wl->selection.target == ec) @@ -864,8 +1125,8 @@ e_comp_wl_data_manager_init(void) { /* try to create global data manager */ e_comp_wl->mgr.global = - wl_global_create(e_comp_wl->wl.disp, &wl_data_device_manager_interface, 1, - e_comp->wl_comp_data, _e_comp_wl_data_cb_bind_manager); + wl_global_create(e_comp_wl->wl.disp, &wl_data_device_manager_interface, 3, + NULL, _e_comp_wl_data_cb_bind_manager); if (!e_comp_wl->mgr.global) { ERR("Could not create global for data device manager"); @@ -917,7 +1178,7 @@ e_comp_wl_data_manager_source_create(struct wl_client *client, struct wl_resourc source->cancelled = _e_comp_wl_data_source_cancelled_send; source->resource = - wl_resource_create(client, &wl_data_source_interface, 1, id); + wl_resource_create(client, &wl_data_source_interface, wl_resource_get_version(resource), id); if (!source->resource) { ERR("Could not create data source resource"); diff --git a/src/bin/e_comp_wl_data.h b/src/bin/e_comp_wl_data.h index 478178004..d9bf888ac 100644 --- a/src/bin/e_comp_wl_data.h +++ b/src/bin/e_comp_wl_data.h @@ -47,6 +47,14 @@ struct _E_Comp_Wl_Data_Source 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); + E_Comp_Wl_Data_Offer *offer; + uint32_t dnd_actions; + enum wl_data_device_manager_dnd_action current_dnd_action; + enum wl_data_device_manager_dnd_action compositor_action; + uint32_t serial; + + Eina_Bool accepted : 1; + Eina_Bool actions_set : 1; }; struct _E_Comp_Wl_Data_Offer @@ -55,6 +63,9 @@ struct _E_Comp_Wl_Data_Offer E_Comp_Wl_Data_Source *source; //indicates source client data struct wl_listener source_destroy_listener; //listener for destroy of source + uint32_t dnd_actions; + enum wl_data_device_manager_dnd_action preferred_dnd_action; + Eina_Bool in_ask : 1; }; struct _E_Comp_Wl_Clipboard_Source