You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1298 lines
40 KiB

/*
* Copyright © 2011 Kristian Høgsberg
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice (including the
* next paragraph) shall be included in all copies or substantial
* portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#define EXECUTIVE_MODE_ENABLED
#define E_COMP_WL
#include "e.h"
#if defined(__clang__)
# pragma clang diagnostic ignored "-Wunused-parameter"
#elif (__GNUC__ == 4 && __GNUC_MINOR__ >= 6) || __GNUC__ > 4
# 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)
{
if (!source->mime_types) return;
while (eina_array_count(source->mime_types))
eina_stringshare_del(eina_array_pop(source->mime_types));
eina_array_free(source->mime_types);
}
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;
DBG("Data Offer Accept");
if (!(offer = wl_resource_get_user_data(resource)))
return;
/* 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
_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;
DBG("Data Offer Receive");
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)
{
DBG("Data Offer Destroy");
wl_resource_destroy(resource);
}
static void
data_source_notify_finish(E_Comp_Wl_Data_Source *source)
{
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
data_offer_update_action(E_Comp_Wl_Data_Offer *offer)
{
uint32_t action;
if (!offer->source)
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 = wl_resource_get_user_data(resource);
if (!offer->source)
goto out;
wl_list_remove(&offer->source_destroy_listener.link);
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 */
static void
_e_comp_wl_data_offer_cb_source_destroy(struct wl_listener *listener, void *data EINA_UNUSED)
{
E_Comp_Wl_Data_Offer *offer;
DBG("Data Offer Source Destroy");
if (!listener) return;
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,
data_offer_finish,
data_offer_set_actions,
};
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;
DBG("Data Source Offer");
if (!(source = wl_resource_get_user_data(resource)))
return;
if (!source->mime_types)
source->mime_types = eina_array_new(1);
eina_array_push(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)
{
DBG("Data Source Destroy");
wl_resource_destroy(resource);
}
static void
data_source_set_actions(struct wl_client *client,
struct wl_resource *resource,
uint32_t dnd_actions)
{
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 */
static void
_e_comp_wl_data_source_cb_resource_destroy(struct wl_resource *resource)
{
E_Comp_Wl_Data_Source *source;
if (!(source = wl_resource_get_user_data(resource)))
return;
wl_signal_emit(&source->destroy_signal, source);
_mime_types_free(source);
if (e_comp_wl->drag_source == source)
{
//free the drag here
e_object_del(E_OBJECT(e_comp_wl->drag));
e_comp_wl->drag = NULL;
}
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)
{
DBG("Data Source Target Send");
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)
{
DBG("Data Source Source Send");
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)
{
DBG("Data Source Cancelled Send");
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,
data_source_set_actions,
};
static void
_e_comp_wl_data_device_destroy_selection_data_source(struct wl_listener *listener EINA_UNUSED, void *data)
{
E_Comp_Wl_Data_Source *source;
struct wl_resource *data_device_res = NULL, *focus = NULL;
DBG("Data Device Destroy Selection Source");
if (!(source = (E_Comp_Wl_Data_Source *)data))
return;
e_comp_wl->selection.data_source = NULL;
if (e_comp_wl->kbd.enabled)
focus = e_comp_wl->kbd.focus;
if (focus)
{
if (source->resource)
data_device_res =
e_comp_wl_data_find_for_client(wl_resource_get_client(source->resource));
if (data_device_res)
wl_data_device_send_selection(data_device_res, NULL);
}
wl_signal_emit(&e_comp_wl->selection.signal, e_comp->wl_comp_data);
}
static struct wl_resource *
_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;
char *t;
DBG("Data Offer Create");
offer = E_NEW(E_Comp_Wl_Data_Offer, 1);
if (!offer) return NULL;
offer->resource =
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);
return NULL;
}
wl_resource_set_implementation(offer->resource,
&_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, 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);
return offer->resource;
}
static void
_e_comp_wl_data_device_selection_set(void *data EINA_UNUSED, E_Comp_Wl_Data_Source *source, uint32_t serial)
{
E_Comp_Wl_Data_Source *sel_source;
struct wl_resource *offer_res, *data_device_res, *focus = NULL;
sel_source = (E_Comp_Wl_Data_Source *)e_comp_wl->selection.data_source;
if (sel_source && (e_comp_wl->selection.serial - serial < UINT32_MAX / 2))
{
if ((source) && (!serial))
{
/* drm canvas will always have serial 0 */
pid_t pid;
wl_client_get_credentials(wl_resource_get_client(source->resource), &pid, NULL, NULL);
if (pid != getpid()) return;
}
else return;
}
if (sel_source)
{
if (!e_comp_wl->clipboard.xwl_owner)
wl_list_remove(&e_comp_wl->selection.data_source_listener.link);
cancel wl selections after removing destroy listener cancel may destroy the selection source, resulting in invalid access ==10735== Invalid write of size 8 ==10735== at 0x87C8095: wl_list_remove (wayland-util.c:56) ==10735== by 0x2EE745: _e_comp_wl_data_device_selection_set (e_comp_wl_data.c:506) ==10735== by 0x2EF241: _e_comp_wl_data_device_cb_selection_set (e_comp_wl_data.c:714) ==10735== by 0x1021F037: ffi_call_unix64 (in /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4) ==10735== by 0x1021EA99: ffi_call (in /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4) ==10735== by 0x87C70C0: wl_closure_invoke (connection.c:935) ==10735== by 0x87C13C8: wl_client_connection_data (wayland-server.c:406) ==10735== by 0x87C4483: wl_event_source_fd_dispatch (event-loop.c:90) ==10735== by 0x87C4DE4: wl_event_loop_dispatch (event-loop.c:423) ==10735== by 0x85AE346: _cb_create_data (ecore_wl2_display.c:399) ==10735== by 0x946857A: _ecore_call_fd_cb (ecore_private.h:347) ==10735== by 0x946AE51: _ecore_main_fd_handlers_call (ecore_main.c:2015) ==10735== by 0x946B823: _ecore_main_loop_iterate_internal (ecore_main.c:2403) ==10735== by 0x946935E: ecore_main_loop_begin (ecore_main.c:1308) ==10735== by 0x151139: main (e_main.c:1088) ==10735== Address 0x222ca980 is 16 bytes inside a block of size 136 free'd ==10735== at 0x4C2CE1B: free (vg_replace_malloc.c:530) ==10735== by 0x2F076F: e_comp_wl_clipboard_source_unref (e_comp_wl_data.c:1291) ==10735== by 0x214C6A09: _xwayland_send_cancelled (dnd.c:149) ==10735== by 0x2EE71D: _e_comp_wl_data_device_selection_set (e_comp_wl_data.c:504) ==10735== by 0x2EF241: _e_comp_wl_data_device_cb_selection_set (e_comp_wl_data.c:714) ==10735== by 0x1021F037: ffi_call_unix64 (in /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4) ==10735== by 0x1021EA99: ffi_call (in /usr/lib/x86_64-linux-gnu/libffi.so.6.0.4) ==10735== by 0x87C70C0: wl_closure_invoke (connection.c:935) ==10735== by 0x87C13C8: wl_client_connection_data (wayland-server.c:406) ==10735== by 0x87C4483: wl_event_source_fd_dispatch (event-loop.c:90) ==10735== by 0x87C4DE4: wl_event_loop_dispatch (event-loop.c:423) ==10735== by 0x85AE346: _cb_create_data (ecore_wl2_display.c:399) ==10735== by 0x946857A: _ecore_call_fd_cb (ecore_private.h:347) ==10735== by 0x946AE51: _ecore_main_fd_handlers_call (ecore_main.c:2015) ==10735== by 0x946B823: _ecore_main_loop_iterate_internal (ecore_main.c:2403) ==10735== by 0x946935E: ecore_main_loop_begin (ecore_main.c:1308) ==10735== by 0x151139: main (e_main.c:1088) ==10735== Block was alloc'd at ==10735== at 0x4C2DC05: calloc (vg_replace_malloc.c:711) ==10735== by 0x2F0520: e_comp_wl_clipboard_source_create (e_comp_wl_data.c:1231) ==10735== by 0x214C71C9: _xwl_selection_notify (dnd.c:286) ==10735== by 0x946130C: _ecore_call_handler_cb (ecore_private.h:331) ==10735== by 0x94626BC: _ecore_event_call (ecore_events.c:629) ==10735== by 0x946B83E: _ecore_main_loop_iterate_internal (ecore_main.c:2408) ==10735== by 0x946935E: ecore_main_loop_begin (ecore_main.c:1308) ==10735== by 0x151139: main (e_main.c:1088)
5 years ago
if (sel_source->cancelled)
sel_source->cancelled(sel_source);
e_comp_wl->selection.data_source = NULL;
}
e_comp_wl->selection.data_source = sel_source = source;
e_comp_wl->clipboard.xwl_owner = 0;
e_comp_wl->selection.serial = serial;
if (source) source->serial = serial;
if (e_comp_wl->kbd.enabled)
focus = e_comp_wl->kbd.focus;
if (focus)
{
data_device_res =
e_comp_wl_data_find_for_client(wl_resource_get_client(focus));
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(&e_comp_wl->selection.signal, e_comp->wl_comp_data);
if (source)
{
e_comp_wl->selection.data_source_listener.notify =
_e_comp_wl_data_device_destroy_selection_data_source;
wl_signal_add(&source->destroy_signal,
&e_comp_wl->selection.data_source_listener);
}
}
static void
_e_comp_wl_data_device_drag_finished(E_Drag *drag, int dropped)
{
struct wl_resource *res = NULL;
Evas_Object *o, *z;
E_Comp_Wl_Data_Source *data_source = e_comp_wl->drag_source;
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
{
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;
e_comp_wl->drag = NULL;
e_comp_wl->drag_client = NULL;
e_screensaver_inhibit_toggle(0);
if (dropped) return;
#ifndef HAVE_WAYLAND_ONLY
if (e_comp_wl->selection.target && e_client_has_xwindow(e_comp_wl->selection.target))
{
ecore_x_client_message32_send(e_client_util_win_get(e_comp_wl->selection.target),
ECORE_X_ATOM_XDND_DROP,
ECORE_X_EVENT_MASK_NONE,
e_comp->cm_selection, 0,
ecore_x_current_time_get(), 0, 0);
return;
}
#endif
if (e_comp_wl->selection.target)
res = e_comp_wl_data_find_for_client(wl_resource_get_client(e_comp_wl->selection.target->comp_data->surface));
if (res && 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);
if (res) wl_data_device_send_leave(res);
#ifndef HAVE_WAYLAND_ONLY
if (e_comp_util_has_xwayland())
{
ecore_x_selection_owner_set(0, ECORE_X_ATOM_SELECTION_XDND,
ecore_x_current_time_get());
ecore_x_window_hide(e_comp->cm_selection);
}
#endif
e_comp_wl->selection.target = NULL;
e_comp_wl->drag_source = NULL;
}
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)
{
E_Comp_Wl_Data_Source *source;
Eina_List *l;
struct wl_resource *res;
E_Client *ec = NULL;
int x, y;
DBG("Data Device Drag Start");
if ((e_comp_wl->kbd.focus) && (e_comp_wl->kbd.focus != origin_resource))
return;
if (!(source = wl_resource_get_user_data(source_resource))) return;
e_comp_wl->drag_source = source;
if (icon_resource)
{
DBG("\tHave Icon Resource: %p", icon_resource);
ec = wl_resource_get_user_data(icon_resource);
if (!ec->re_manage)
{
ec->re_manage = 1;
ec->lock_focus_out = ec->override = 1;
ec->icccm.title = eina_stringshare_add("noshadow");
ec->layer = E_LAYER_CLIENT_DRAG;
evas_object_layer_set(ec->frame, E_LAYER_CLIENT_DRAG);
e_client_focus_stack_set(eina_list_remove(e_client_focus_stack_get(), ec));
EC_CHANGED(ec);
e_comp_wl->drag_client = ec;
}
if (ec->comp_data->pending.input)
eina_tiler_clear(ec->comp_data->pending.input);