diff --git a/src/bin/e_comp_wl_data.c b/src/bin/e_comp_wl_data.c index d42fb0c91..3de992036 100644 --- a/src/bin/e_comp_wl_data.c +++ b/src/bin/e_comp_wl_data.c @@ -281,22 +281,40 @@ _e_comp_wl_data_device_drag_finished(E_Drag *drag, int dropped) evas_object_hide(o); evas_object_pass_events_set(o, 1); if (e_comp->wl_comp_data->drag != drag) return; - if (e_comp->wl_comp_data->selection.target && (!dropped)) - { - 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); - } - } e_comp->wl_comp_data->drag = NULL; e_comp->wl_comp_data->drag_client = NULL; - e_comp->wl_comp_data->selection.target = NULL; - e_comp->wl_comp_data->drag_source = NULL; e_screensaver_inhibit_toggle(0); + if (e_comp->wl_comp_data->selection.target && (!dropped)) + { +#ifndef HAVE_WAYLAND_ONLY + if (e_client_has_xwindow(e_comp->wl_comp_data->selection.target)) + { + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->selection.target), + ECORE_X_ATOM_XDND_DROP, ECORE_X_EVENT_MASK_NONE, + e_comp->cm_selection, 0, ecore_x_current_time_get(), 0, 0); + } + else +#endif + { + 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); + } +#ifndef HAVE_WAYLAND_ONLY + if ((e_comp->comp_type != E_PIXMAP_TYPE_X) && e_comp_util_has_x()) + { + ecore_x_selection_owner_set(0, ECORE_X_ATOM_SELECTION_XDND, 0); + ecore_x_window_hide(e_comp->cm_selection); + } +#endif + e_comp->wl_comp_data->selection.target = NULL; + e_comp->wl_comp_data->drag_source = NULL; + } + } } static void @@ -346,6 +364,13 @@ _e_comp_wl_data_device_cb_drag_start(struct wl_client *client, struct wl_resourc if (ec) e_drag_object_set(e_comp->wl_comp_data->drag, ec->frame); e_drag_start(e_comp->wl_comp_data->drag, x, y); +#ifndef HAVE_WAYLAND_ONLY + if ((e_comp->comp_type != E_PIXMAP_TYPE_X) && e_comp_util_has_x()) + { + ecore_x_window_show(e_comp->cm_selection); + ecore_x_selection_owner_set(e_comp->cm_selection, ECORE_X_ATOM_SELECTION_XDND, 0); + } +#endif if (e_comp->wl_comp_data->ptr.ec) e_comp_wl_data_device_send_enter(e_comp->wl_comp_data->ptr.ec); e_screensaver_inhibit_toggle(1); @@ -675,38 +700,60 @@ e_comp_wl_data_device_send_enter(E_Client *ec) uint32_t serial; int x, y; - if (e_client_has_xwindow(ec)) return; - 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_comp_data->selection.data_source && (!offer_res)) return; + if (e_client_has_xwindow(ec) && e_client_has_xwindow(e_comp->wl_comp_data->drag_client)) return; + if (!e_client_has_xwindow(ec)) + { + 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_comp_data->drag_source && (!offer_res)) return; + } 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)) + if (e_client_has_xwindow(ec)) { int d1 = 0x5UL, d2, d3, d4; + E_Comp_Wl_Data_Source *source; + Eina_List *l; d2 = d3 = d4 = 0; + source = e_comp->wl_comp_data->drag_source; - if (e_comp->wl_comp_data->drag->num_types > 3) - d1 |= 0x1UL; + if (eina_list_count(source->mime_types) > 3) + { + const char *type, *types[eina_list_count(source->mime_types)]; + int i = 0; + + d1 |= 0x1UL; + EINA_LIST_FOREACH(source->mime_types, l, type) + types[i++] = type; + ecore_x_dnd_types_set(e_comp->cm_selection, types, i); + } else { - if (e_comp->wl_comp_data->drag->num_types > 0) + l = source->mime_types; + + if (source->mime_types) 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]); + if (eina_list_count(source->mime_types) > 1) + { + l = eina_list_next(l); + d3 = ecore_x_atom_get(eina_list_data_get(l)); + } + if (eina_list_count(source->mime_types) > 2) + { + l = eina_list_next(l); + d4 = ecore_x_atom_get(eina_list_data_get(l)); + } } - ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), + ecore_x_client_message32_send(e_client_util_win_get(ec), ECORE_X_ATOM_XDND_ENTER, ECORE_X_EVENT_MASK_NONE, e_comp->cm_selection, d1, d2, d3, d4); + return; } #endif x = wl_fixed_to_int(e_comp->wl_comp_data->ptr.x) - e_comp->wl_comp_data->selection.target->client.x; @@ -721,16 +768,17 @@ e_comp_wl_data_device_send_leave(E_Client *ec) { struct wl_resource *res; - if (e_client_has_xwindow(ec)) return; + if (e_client_has_xwindow(ec) && e_client_has_xwindow(e_comp->wl_comp_data->drag_client)) return; 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)) + if (e_client_has_xwindow(ec)) { - ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->drag_client), + ecore_x_client_message32_send(e_client_util_win_get(ec), ECORE_X_ATOM_XDND_LEAVE, ECORE_X_EVENT_MASK_NONE, e_comp->cm_selection, 0, 0, 0, 0); + return; } #endif res = e_comp_wl_data_find_for_client(wl_resource_get_client(ec->comp_data->surface)); diff --git a/src/bin/e_dnd.c b/src/bin/e_dnd.c index 167515e15..ea74c1c9b 100644 --- a/src/bin/e_dnd.c +++ b/src/bin/e_dnd.c @@ -1218,8 +1218,9 @@ _e_dnd_cb_mouse_move(void *data EINA_UNUSED, int type EINA_UNUSED, void *event) { 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), + if (!e_client_has_xwindow(e_comp->wl_comp_data->ptr.ec)) return ECORE_CALLBACK_RENEW; + if (e_client_has_xwindow(e_comp->wl_comp_data->drag_client)) return ECORE_CALLBACK_RENEW; + ecore_x_client_message32_send(e_client_util_win_get(e_comp->wl_comp_data->ptr.ec), 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); diff --git a/src/modules/xwayland/dnd.c b/src/modules/xwayland/dnd.c index 0b89dd494..6256e9679 100644 --- a/src/modules/xwayland/dnd.c +++ b/src/modules/xwayland/dnd.c @@ -3,14 +3,39 @@ #include #define WL_TEXT_STR "text/plain;charset=utf-8" +#define INCR_CHUNK_SIZE 1 << 17 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 Ecore_X_Atom timestamp_atom; +static Ecore_X_Atom incr_atom; +static Ecore_X_Atom int_atom; static int32_t cur_fd = -1; static Eina_List *handlers; +static Eina_Hash *pipes; + +typedef struct +{ + Ecore_Fd_Handler *fdh; + E_Comp_Wl_Data_Source *source; + Ecore_X_Window win; + Ecore_X_Atom atom; + Ecore_X_Atom selection; + Ecore_X_Atom property; + Eina_Binbuf *buf; + Eina_Bool incr : 1; +} Pipe; + +static void +_pipe_free(Pipe *p) +{ + ecore_main_fd_handler_del(p->fdh); + eina_binbuf_free(p->buf); + free(p); +} static void _xdnd_finish(Eina_Bool success) @@ -19,6 +44,24 @@ _xdnd_finish(Eina_Bool success) e_comp->cm_selection, !!success, (!!success) * ECORE_X_ATOM_XDND_ACTION_COPY, 0, 0); } +static void +_incr_update(Pipe *p, Eina_Bool success) +{ + ecore_x_selection_notify_send(p->win, p->selection, p->atom, (!!success) * p->property, 0); +} + +static void +_incr_upload(Pipe *p) +{ + size_t size; + + size = eina_binbuf_length_get(p->buf); + size = MIN(size, INCR_CHUNK_SIZE); + ecore_x_window_prop_property_set(p->win, p->property, p->atom, 8, (void*)eina_binbuf_string_get(p->buf), size); + eina_binbuf_free(p->buf); + p->buf = NULL; +} + static void _xwayland_dnd_finish(void) { @@ -46,8 +89,6 @@ _xwayland_drop(E_Drag *drag, int dropped) { 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; } @@ -90,6 +131,7 @@ _xwayland_cancelled_send(E_Comp_Wl_Data_Source *source) static Eina_Bool _xwl_fixes_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Fixes_Selection_Notify *ev) { + if (ev->owner == e_comp->cm_selection) return ECORE_CALLBACK_RENEW; if (ev->atom == ECORE_X_ATOM_SELECTION_XDND) { if (ev->owner) @@ -189,6 +231,125 @@ _xwl_selection_notify(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Sele return ECORE_CALLBACK_RENEW; } +static Eina_Bool +_xwl_pipe_read(void *data, Ecore_Fd_Handler *fdh) +{ + Pipe *p = data; + ssize_t len; + unsigned char *buf; + + buf = malloc(INCR_CHUNK_SIZE); + len = read(ecore_main_fd_handler_fd_get(fdh), (void*)buf, INCR_CHUNK_SIZE); + if (len < 0) + { + _incr_update(p, 0); + eina_hash_del_by_key(pipes, &p->win); + } + if (len == INCR_CHUNK_SIZE) + { + p->buf = eina_binbuf_manage_new(buf, len, 0); + if (p->incr) + _incr_upload(p); + else + { + unsigned long size = INCR_CHUNK_SIZE; + + p->incr = 1; + ecore_x_window_prop_property_set(p->win, p->atom, incr_atom, 32, &size, 1); + _incr_update(p, 1); + } + ecore_main_fd_handler_active_set(p->fdh, 0); + return ECORE_CALLBACK_RENEW; + } + if (len) + p->buf = eina_binbuf_manage_new(buf, len, 0); + _incr_upload(p); + if (p->incr) + ecore_main_fd_handler_active_set(p->fdh, 0); + else + { + _incr_update(p, 1); + eina_hash_del_by_key(pipes, &p->win); + } + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_xwl_selection_request(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Selection_Request *ev) +{ + E_Comp_Wl_Data_Source *source; + const char *type; + Eina_List *l; + + if (!e_comp->wl_comp_data->drag_source) return ECORE_CALLBACK_RENEW; + + source = e_comp->wl_comp_data->drag_source; + if (ev->target == ECORE_X_ATOM_SELECTION_TARGETS) + { + Ecore_X_Atom *atoms; + int i = 0; + + atoms = alloca((2 + eina_list_count(source->mime_types)) * sizeof(void*)); + EINA_LIST_FOREACH(source->mime_types, l, type) + atoms[i++] = ecore_x_atom_get(type); + atoms[i++] = timestamp_atom; + atoms[i++] = ECORE_X_ATOM_SELECTION_TARGETS; + ecore_x_window_prop_property_set(ev->requestor, ev->property, ECORE_X_ATOM_ATOM, 32, atoms, i); + ecore_x_selection_notify_send(ev->requestor, ev->selection, ev->target, ev->property, 0); + } + else if (ev->target == timestamp_atom) + { + Ecore_X_Time timestamp; + + timestamp = ecore_x_current_time_get(); + ecore_x_window_prop_property_set(ev->requestor, ev->property, int_atom, 32, (void*)×tamp, 1); + ecore_x_selection_notify_send(ev->requestor, ev->selection, ev->target, ev->property, 0); + } + else + { + const char *name; + Pipe *p; + + name = ecore_x_atom_name_get(ev->target); + EINA_LIST_FOREACH(source->mime_types, l, type) + if (eina_streq(name, type)) + { + E_Client *ec; + int fds[2]; + + p = E_NEW(Pipe, 1); + socketpair(AF_UNIX, (SOCK_STREAM | SOCK_CLOEXEC), 0, fds); + fcntl(fds[0], F_SETFL, O_NONBLOCK); + p->fdh = ecore_main_fd_handler_add(fds[0], ECORE_FD_READ, _xwl_pipe_read, p, NULL, NULL); + p->win = ev->requestor; + p->source = source; + wl_data_source_send_send(source->resource, type, dup(fds[1])); + close(fds[1]); + p->atom = ev->target; + p->selection = ev->selection; + p->property = ev->property; + ec = e_pixmap_find_client(E_PIXMAP_TYPE_X, ev->requestor); + if (ec && ec->override) + ecore_x_window_sniff(ev->requestor); + eina_hash_add(pipes, &p->win, p); + break; + } + } + return ECORE_CALLBACK_RENEW; +} + +static Eina_Bool +_xwl_property(void *d EINA_UNUSED, int t EINA_UNUSED, Ecore_X_Event_Window_Property *ev) +{ + Pipe *p; + + p = eina_hash_find(pipes, &ev->win); + if (!p) return ECORE_CALLBACK_RENEW; + /* FIXME: WHO FORGOT THE FUCKING STATE FLAG???? */ + ecore_main_fd_handler_active_set(p->fdh, ECORE_FD_READ); + return ECORE_CALLBACK_RENEW; +} + EINTERN void dnd_init(void) { @@ -196,9 +357,15 @@ dnd_init(void) ecore_x_fixes_selection_notification_request(ECORE_X_ATOM_SELECTION_XDND); E_LIST_HANDLER_APPEND(handlers, ECORE_X_EVENT_FIXES_SELECTION_NOTIFY, _xwl_fixes_selection_notify, NULL); E_LIST_HANDLER_APPEND(handlers, ECORE_X_EVENT_SELECTION_NOTIFY, _xwl_selection_notify, NULL); + E_LIST_HANDLER_APPEND(handlers, ECORE_X_EVENT_SELECTION_REQUEST, _xwl_selection_request, NULL); + E_LIST_HANDLER_APPEND(handlers, ECORE_X_EVENT_WINDOW_PROPERTY, _xwl_property, NULL); xconvertselection = dlsym(NULL, "XConvertSelection"); string_atom = ecore_x_atom_get("UTF8_STRING"); + timestamp_atom = ecore_x_atom_get("TIMESTAMP"); + int_atom = ecore_x_atom_get("INTEGER"); + incr_atom = ecore_x_atom_get("TIMESTAMP"); xwl_dnd_atom = ecore_x_atom_get("E_XWL_DND_ATOM_HAHA"); + pipes = eina_hash_int32_new((Eina_Free_Cb)_pipe_free); e_comp_shape_queue(); } @@ -206,4 +373,5 @@ EINTERN void dnd_shutdown(void) { E_FREE_LIST(handlers, ecore_event_handler_del); + E_FREE_FUNC(pipes, eina_hash_free); }