From 5bde1a864838d9c86cf5cf59564e3efaf1389393 Mon Sep 17 00:00:00 2001 From: Mike Blumenkrantz Date: Thu, 6 Aug 2015 19:26:08 -0400 Subject: [PATCH] implement x11->wayland dnd operations still a little rough, but the basics are functional. works by showing the x11 compositor selection window, which has rects to exclude geometries of xwl clients, for getting x11 xdnd events, and then manually sending all the related client messages in order to inform the x11 client that enlightenment is, in fact, an extremely credible xdnd drop site and not a rogue compositor which will mangle/destroy the dnd data. still render crashes after the operation completes, so possibly not the most useful thing to be using now --- src/bin/e_comp_wl_data.c | 35 +++++- src/bin/e_dnd.c | 15 +++ src/modules/Makefile_xwayland.mk | 3 +- src/modules/xwayland/dnd.c | 202 ++++++++++++++++++++++++++++++ src/modules/xwayland/e_mod_main.c | 3 + 5 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 src/modules/xwayland/dnd.c diff --git a/src/bin/e_comp_wl_data.c b/src/bin/e_comp_wl_data.c index 11d5c7841..d42fb0c91 100644 --- a/src/bin/e_comp_wl_data.c +++ b/src/bin/e_comp_wl_data.c @@ -685,6 +685,30 @@ e_comp_wl_data_device_send_enter(E_Client *ec) e_comp->wl_comp_data->selection.target = ec; evas_object_event_callback_add(ec->frame, EVAS_CALLBACK_DEL, _e_comp_wl_data_device_target_del, ec); +#ifndef HAVE_WAYLAND_ONLY + if (e_client_has_xwindow(e_comp->wl_comp_data->drag_client)) + { + int d1 = 0x5UL, d2, d3, d4; + + d2 = d3 = d4 = 0; + + if (e_comp->wl_comp_data->drag->num_types > 3) + d1 |= 0x1UL; + else + { + if (e_comp->wl_comp_data->drag->num_types > 0) + d2 = ecore_x_atom_get(e_comp->wl_comp_data->drag->types[0]); + if (e_comp->wl_comp_data->drag->num_types > 1) + d3 = ecore_x_atom_get(e_comp->wl_comp_data->drag->types[1]); + if (e_comp->wl_comp_data->drag->num_types > 2) + d4 = ecore_x_atom_get(e_comp->wl_comp_data->drag->types[2]); + } + + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), + ECORE_X_ATOM_XDND_ENTER, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, d1, d2, d3, d4); + } +#endif x = wl_fixed_to_int(e_comp->wl_comp_data->ptr.x) - e_comp->wl_comp_data->selection.target->client.x; y = wl_fixed_to_int(e_comp->wl_comp_data->ptr.y) - e_comp->wl_comp_data->selection.target->client.y; serial = wl_display_next_serial(e_comp->wl_comp_data->wl.disp); @@ -701,8 +725,17 @@ e_comp_wl_data_device_send_leave(E_Client *ec) evas_object_event_callback_del_full(ec->frame, EVAS_CALLBACK_DEL, _e_comp_wl_data_device_target_del, ec); if (e_comp->wl_comp_data->selection.target == ec) e_comp->wl_comp_data->selection.target = NULL; +#ifndef HAVE_WAYLAND_ONLY + if (e_client_has_xwindow(e_comp->wl_comp_data->drag_client)) + { + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), + ECORE_X_ATOM_XDND_LEAVE, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, 0, 0, 0, 0); + } +#endif res = e_comp_wl_data_find_for_client(wl_resource_get_client(ec->comp_data->surface)); - wl_data_device_send_leave(res); + if (res) + wl_data_device_send_leave(res); } EINTERN void * diff --git a/src/bin/e_dnd.c b/src/bin/e_dnd.c index a8f5d69aa..167515e15 100644 --- a/src/bin/e_dnd.c +++ b/src/bin/e_dnd.c @@ -227,6 +227,8 @@ e_dnd_init(void) if (e_comp->comp_type == E_PIXMAP_TYPE_X) e_drop_xdnd_register_set(e_comp->ee_win, 1); + else + e_drop_xdnd_register_set(e_comp->cm_selection, 1); _action = ECORE_X_ATOM_XDND_ACTION_PRIVATE; #endif @@ -1211,6 +1213,18 @@ _e_dnd_cb_mouse_move(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) if (!_xdnd) _e_drag_update(_drag_win_root, ev->x, ev->y, _action ?: ECORE_X_ATOM_XDND_ACTION_PRIVATE); +# ifdef HAVE_WAYLAND + if ((e_comp->comp_type == E_PIXMAP_TYPE_WL) && e_comp_util_has_x()) + { + if (e_comp->wl_comp_data->drag != _drag_current) return ECORE_CALLBACK_RENEW; + if (!e_comp->wl_comp_data->ptr.ec) return ECORE_CALLBACK_RENEW; + if (e_client_has_xwindow(e_comp->wl_comp_data->ptr.ec)) return ECORE_CALLBACK_RENEW; + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), + ECORE_X_ATOM_XDND_POSITION, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, 0, ((ev->x << 16) & 0xffff0000) | (ev->y & 0xffff), + ev->timestamp, ECORE_X_ATOM_XDND_ACTION_COPY); + } +# endif #endif return ECORE_CALLBACK_PASS_ON; @@ -1421,6 +1435,7 @@ _e_dnd_cb_event_dnd_selection(void *data EINA_UNUSED, int type EINA_UNUSED, void if (!eina_hash_find(_drop_win_hash, &ev->win)) return ECORE_CALLBACK_PASS_ON; if (ev->selection != ECORE_X_SELECTION_XDND) return ECORE_CALLBACK_PASS_ON; + if (e_comp->comp_type != E_PIXMAP_TYPE_X) return ECORE_CALLBACK_RENEW; if (!_xdnd) { diff --git a/src/modules/Makefile_xwayland.mk b/src/modules/Makefile_xwayland.mk index 5afc15b56..56fd137fa 100644 --- a/src/modules/Makefile_xwayland.mk +++ b/src/modules/Makefile_xwayland.mk @@ -13,7 +13,8 @@ src_modules_xwayland_module_la_LIBADD = $(MOD_LIBS) @XWAYLAND_LIBS@ @WAYLAND_L src_modules_xwayland_module_la_LDFLAGS = $(MOD_LDFLAGS) src_modules_xwayland_module_la_SOURCES = \ -src/modules/xwayland/e_mod_main.c +src/modules/xwayland/e_mod_main.c \ +src/modules/xwayland/dnd.c PHONIES += xwayland install-xwayland xwayland: $(xwaylandpkg_LTLIBRARIES) diff --git a/src/modules/xwayland/dnd.c b/src/modules/xwayland/dnd.c new file mode 100644 index 000000000..69ae9b515 --- /dev/null +++ b/src/modules/xwayland/dnd.c @@ -0,0 +1,202 @@ +#define E_COMP_WL +#include "e.h" +#include + +#define WL_TEXT_STR "text/plain;charset=utf-8" + +static void (*xconvertselection)(Ecore_X_Display *, Ecore_X_Atom, Ecore_X_Atom, Ecore_X_Atom, Ecore_X_Window, Ecore_X_Time); +static Ecore_X_Atom string_atom; +static Ecore_X_Atom xwl_dnd_atom; + +static int32_t cur_fd = -1; + +static void +_xdnd_finish(Eina_Bool success) +{ + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), ECORE_X_ATOM_XDND_FINISHED, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, !!success, (!!success) * ECORE_X_ATOM_XDND_ACTION_COPY, 0, 0); +} + +static void +_xwayland_dnd_finish(void) +{ + ecore_x_window_hide(e_comp->cm_selection); + ecore_x_window_prop_property_del(e_comp->cm_selection, ECORE_X_ATOM_XDND_TYPE_LIST); + e_comp->wl_comp_data->drag_client = NULL; + e_comp->wl_comp_data->drag_source = NULL; + cur_fd = -1; +} + +static void +_xwayland_drop(E_Drag *drag, int dropped) +{ + if (e_comp->comp_type != E_PIXMAP_TYPE_WL) return; + e_comp->wl_comp_data->drag = NULL; + if (e_object_is_del(E_OBJECT(drag)) || (!e_comp->wl_comp_data->selection.target)) + _xdnd_finish(0); + else + { + struct wl_resource *res; + + res = e_comp_wl_data_find_for_client(wl_resource_get_client(e_comp->wl_comp_data->selection.target->comp_data->surface)); + if (res) + { + wl_data_device_send_drop(res); + wl_data_device_send_leave(res); + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), ECORE_X_ATOM_XDND_DROP, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, 0, ecore_x_current_time_get(), 0, 0); + } + return; + } + _xwayland_dnd_finish(); +} + +static void +_xwayland_target_send(E_Comp_Wl_Data_Source *source, uint32_t serial EINA_UNUSED, const char* mime_type) +{ + DBG("XWL Data Source Target Send"); + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), ECORE_X_ATOM_XDND_STATUS, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, 2 | !!mime_type, 0, 0, (!!mime_type) * ECORE_X_ATOM_XDND_ACTION_COPY); +} + +static void +_xwayland_send_send(E_Comp_Wl_Data_Source *source, const char* mime_type, int32_t fd) +{ + Ecore_X_Atom type; + + DBG("XWL Data Source Source Send"); + + _xdnd_finish(0); + + if (eina_streq(mime_type, WL_TEXT_STR)) + type = string_atom; + else + type = ecore_x_atom_get(mime_type); + + cur_fd = fd; + xconvertselection(ecore_x_display_get(), ECORE_X_ATOM_SELECTION_XDND, type, xwl_dnd_atom, e_comp->cm_selection, 0); +} + +static void +_xwayland_cancelled_send(E_Comp_Wl_Data_Source *source) +{ + DBG("XWL Data Source Cancelled Send"); + e_object_del(E_OBJECT(e_comp->wl_comp_data->drag)); +} + +static Eina_Bool +_xwl_fixes_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Fixes_Selection_Notify *ev) +{ + if (ev->atom == ECORE_X_ATOM_SELECTION_XDND) + { + if (ev->owner) + { + int x, y, num; + unsigned char *data; + const char **names = NULL; + Eina_List *namelist = NULL; + E_Comp_Wl_Data_Source *source; + + if (ecore_x_window_prop_property_get(ev->owner, + ECORE_X_ATOM_XDND_TYPE_LIST, + ECORE_X_ATOM_ATOM, + 32, + &data, + &num)) + { + int i; + Ecore_X_Atom *types = (void*)data; + + names = malloc(num * sizeof(void*)); + for (i = 0; i < num; i++) + { + const char *name; + + if (types[i] == string_atom) + { + name = names[i] = "UTF8_STRING"; + namelist = eina_list_append(namelist, eina_stringshare_add(WL_TEXT_STR)); + } + else + names[i] = name = ecore_x_atom_name_get(types[i]); + namelist = eina_list_append(namelist, eina_stringshare_add(name)); + } + if (num > 3) + { + ecore_x_window_prop_property_set(e_comp->cm_selection, + ECORE_X_ATOM_XDND_TYPE_LIST, ECORE_X_ATOM_ATOM, 32, names, num); + } + + free(data); + } + evas_pointer_canvas_xy_get(e_comp->evas, &x, &y); + e_comp->wl_comp_data->drag_client = e_pixmap_find_client(E_PIXMAP_TYPE_X, ev->owner); + e_comp->wl_comp_data->drag = e_drag_new(x, y, names, num, NULL, 0, NULL, _xwayland_drop); + ecore_x_window_move_resize(e_comp->cm_selection, 0, 0, e_comp->w, e_comp->h); + ecore_x_window_show(e_comp->cm_selection); + e_drag_start(e_comp->wl_comp_data->drag, x, y); + if (e_comp->wl_comp_data->ptr.ec) + e_comp_wl_data_device_send_enter(e_comp->wl_comp_data->ptr.ec); + e_comp_canvas_feed_mouse_up(0); + source = e_comp_wl_data_manager_source_create(e_comp->wl_comp_data->xwl_client, + e_comp->wl_comp_data->mgr.resource, 1); + e_comp->wl_comp_data->drag_source = source; + source->target = _xwayland_target_send; + source->send = _xwayland_send_send; + source->cancelled = _xwayland_cancelled_send; + source->mime_types = namelist; + free(names); + } + else + { + if (e_comp->wl_comp_data->drag) + e_object_del(E_OBJECT(e_comp->wl_comp_data->drag)); + ecore_x_window_hide(e_comp->cm_selection); + e_comp->wl_comp_data->drag = NULL; + e_comp->wl_comp_data->drag_client = NULL; + } + e_screensaver_inhibit_toggle(!!ev->owner); + return ECORE_CALLBACK_RENEW; + } + //if (ev->atom == ECORE_X_ATOM_SELECTION_CLIPBOARD) + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_xwl_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Selection_Notify *ev) +{ + Ecore_X_Selection_Data *sd; + int wrote = 0; + + DBG("XWL SELECTION NOTIFY"); + if ((ev->selection != ECORE_X_SELECTION_XDND) && (ev->selection == ECORE_X_SELECTION_CLIPBOARD)) + { + e_object_del(E_OBJECT(e_comp->wl_comp_data->drag)); + return ECORE_CALLBACK_RENEW; + } + /* FIXME: ecore-x events are fucked */ + //if (ecore_x_atom_get(ev->target) != xwl_dnd_atom) return ECORE_CALLBACK_RENEW; + sd = ev->data; + + do + { + wrote += write(cur_fd, sd->data, sd->length); + } while (wrote < sd->length); + _xdnd_finish(1); + close(cur_fd); + _xwayland_dnd_finish(); + return ECORE_CALLBACK_RENEW; +} + +EINTERN void +dnd_init(void) +{ + ecore_x_fixes_selection_notification_request(ecore_x_atom_get("CLIPBOARD")); + ecore_x_fixes_selection_notification_request(ECORE_X_ATOM_SELECTION_XDND); + ecore_event_handler_add(ECORE_X_EVENT_FIXES_SELECTION_NOTIFY, (Ecore_Event_Handler_Cb)_xwl_fixes_selection_notify, NULL); + ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY, (Ecore_Event_Handler_Cb)_xwl_selection_notify, NULL); + xconvertselection = dlsym(NULL, "XConvertSelection"); + string_atom = ecore_x_atom_get("UTF8_STRING"); + xwl_dnd_atom = ecore_x_atom_get("E_XWL_DND_ATOM_HAHA"); + e_comp_shape_queue(); +} diff --git a/src/modules/xwayland/e_mod_main.c b/src/modules/xwayland/e_mod_main.c index 5926ad691..8cacc27db 100644 --- a/src/modules/xwayland/e_mod_main.c +++ b/src/modules/xwayland/e_mod_main.c @@ -3,6 +3,8 @@ #include #include +EINTERN void dnd_init(void); + /* local structures */ typedef struct _E_XWayland_Server E_XWayland_Server; struct _E_XWayland_Server @@ -256,6 +258,7 @@ xnotify(void *d EINA_UNUSED, Ecore_Thread *eth EINA_UNUSED, void *disp) } assert(ecore_x_init_from_display(disp)); e_comp_x_init(); + dnd_init(); } static void