ecore_evas: introduce wayland support for cnp & dnd

This adds cnp support, actions are right now only mapped to "ask",
further support can be added there, and synchronization can be added to
register more available actions. However, i did not find *any* wayland
implementation in gtk qt nor chromiumos that even use the action to
indicate anything. This here also has a slightly different behaviour to
X11 in terms of coordinates for motion,leave,enter. They can contain
negative coordinates (which is due to the fact that wl is CSD and X11 is
SSD. However, I did not want to fix this in any regard, as you might
want to use that, and it would be a none trivial amount of code to fix
that.

Reviewed-by: Chris Michael <cp.michael@samsung.com>
Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Differential Revision: https://phab.enlightenment.org/D11329
This commit is contained in:
Marcel Hollerbach 2020-02-10 09:20:18 +01:00
parent e0c40abb40
commit 5ac02ec9ac
2 changed files with 482 additions and 5 deletions

View File

@ -4,6 +4,7 @@
#include "ecore_evas_wayland_private.h"
#include <Evas_Engine_Wayland.h>
#include "ecore_wl2_internal.h"
extern EAPI Eina_List *_evas_canvas_image_data_unset(Evas *eo_e);
extern EAPI void _evas_canvas_image_data_regenerate(Eina_List *list);
@ -32,6 +33,7 @@ static Eina_Array *_ecore_evas_wl_event_hdls;
static void _ecore_evas_wayland_resize(Ecore_Evas *ee, int location);
static void _ecore_evas_wl_common_rotation_set(Ecore_Evas *ee, int rotation, int resize);
static void _ecore_evas_wl_selection_init(Ecore_Evas *ee);
/* local functions */
static void
@ -1440,6 +1442,15 @@ _ecore_evas_wl_common_free(Ecore_Evas *ee)
if (wdata->frame) ecore_wl2_window_frame_callback_del(wdata->frame);
wdata->frame = NULL;
ecore_event_handler_del(wdata->sync_handler);
ecore_event_handler_del(wdata->changed_handler);
ecore_event_handler_del(wdata->send_handler);
ecore_event_handler_del(wdata->offer_handler);
ecore_event_handler_del(wdata->dnd_leave_handler);
ecore_event_handler_del(wdata->dnd_enter_handler);
ecore_event_handler_del(wdata->dnd_motion_handler);
ecore_event_handler_del(wdata->dnd_drop_handler);
ecore_event_handler_del(wdata->dnd_end_handler);
if (wdata->win)
{
ecore_wl2_window_close_callback_set(wdata->win, NULL, NULL);
@ -2389,6 +2400,457 @@ _ecore_wl2_devices_setup(Ecore_Evas *ee, Ecore_Wl2_Display *display)
return r;
}
static void
_reeval_seat(unsigned int *seat, Ecore_Evas *ee)
{
if (*seat == 0)
*seat = evas_device_seat_id_get(evas_default_device_get(ee->evas, EFL_INPUT_DEVICE_TYPE_SEAT));
}
static inline void
_clear_selection(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection)
{
Ecore_Evas_Engine_Wl_Data *edata = ee->engine.data;
Ecore_Evas_Selection_Callbacks *cbs = &edata->selection_data[selection].callbacks;
EINA_SAFETY_ON_FALSE_RETURN(cbs->cancel);
cbs->cancel(ee, seat, selection);
eina_array_free(cbs->available_types);
memset(cbs, 0, sizeof(Ecore_Evas_Selection_Callbacks));
}
static void
_store_selection_cbs(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel)
{
Ecore_Evas_Wl_Selection_Data *sdata;
Ecore_Evas_Engine_Wl_Data *edata;
Ecore_Evas_Selection_Callbacks *cbs;
edata = ee->engine.data;
sdata = &edata->selection_data[selection];
cbs = &sdata->callbacks;
if (cbs->cancel)
{
_clear_selection(ee, seat, selection);
}
cbs->delivery = delivery;
cbs->cancel = cancel;
cbs->available_types = available_types;
}
static inline Ecore_Wl2_Input*
_fetch_input(Ecore_Evas *ee, unsigned int seat)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
return ecore_wl2_display_input_find(ecore_wl2_window_display_get(wdata->win), seat);
}
static Eina_Bool
_ecore_evas_wl_selection_claim(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Ecore_Evas_Wl_Selection_Data *data = &wdata->selection_data[selection];
char *tmp_array[eina_array_count(available_types) + 1];
if (selection == ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER)
{
_store_selection_cbs(ee, seat, selection, available_types, delivery, cancel);
return EINA_TRUE;
}
_reeval_seat(&seat, ee);
_store_selection_cbs(ee, seat, selection, available_types, delivery, cancel);
for (unsigned int i = 0; i < eina_array_count(available_types); ++i)
{
tmp_array[i] = eina_array_data_get(available_types, i);
}
tmp_array[eina_array_count(available_types)] = NULL;
data->sent_serial = ecore_wl2_dnd_selection_set(_fetch_input(ee, seat), (const char**)tmp_array);
return EINA_TRUE;
}
static Eina_Future*
_ecore_evas_wl_selection_request(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *acceptable_types)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Ecore_Evas_Wl_Selection_Data *data = &wdata->selection_data[selection];
Ecore_Wl2_Input *input;
Ecore_Wl2_Offer *offer = NULL;
Eina_Future *future;
_reeval_seat(&seat, ee);
input = _fetch_input(ee, seat);
if (data->delivery)
{
eina_promise_reject(data->delivery, ecore_evas_request_replaced);
data->delivery = NULL;
}
data->delivery = efl_loop_promise_new(efl_main_loop_get());
future = eina_future_new(data->delivery);
if (selection == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER)
{
offer = data->offer = wdata->external_offer;
}
else if (selection == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER)
{
offer = data->offer = ecore_wl2_dnd_selection_get(input);
}
if (!offer)
{
eina_promise_reject(data->delivery, ecore_evas_no_selection);
data->delivery = NULL;
}
else
{
Eina_Array *available_types = ecore_wl2_offer_mimes_get(offer);
char *selected_type = NULL;
for (unsigned int i = 0; i < eina_array_count(available_types) && !selected_type; ++i)
{
char *available_type = eina_array_data_get(available_types, i);
for (unsigned int j = 0; j < eina_array_count(acceptable_types) && !selected_type; ++j)
{
char *acceptable_type = eina_array_data_get(acceptable_types, j);
if (eina_streq(acceptable_type, available_type))
{
selected_type = available_type;
data->later_convert = NULL;
break;
}
const char *convert_available_type;
Eina_Iterator *convertions = eina_content_converter_possible_conversions(available_type);
EINA_ITERATOR_FOREACH(convertions, convert_available_type)
{
if (eina_streq(convert_available_type, acceptable_type))
{
selected_type = available_type;
data->later_convert = acceptable_type;
}
}
eina_iterator_free(convertions);
}
}
if (selected_type)
{
ecore_wl2_offer_receive(offer, selected_type);
ecore_wl2_display_flush(ecore_wl2_input_display_get(input));
}
else
{
eina_promise_reject(data->delivery, ecore_evas_no_matching_type);
data->delivery = NULL;
}
}
return future;
}
static Eina_Bool
_ecore_evas_wl_selection_has_owner(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection EINA_UNUSED)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Ecore_Wl2_Input *input;
_reeval_seat(&seat, ee);
input = _fetch_input(ee, seat);
if (selection == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER )
return !!ecore_wl2_dnd_selection_get(input);
else if (selection == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER)
{
Ecore_Evas_Wl_Selection_Data *data = &wdata->selection_data[selection];
return !!data->offer;
}
return EINA_FALSE; //the selection buffer is not supportet in wayland
}
static Eina_Bool
_wl_selection_changed(void *data, int type EINA_UNUSED, void *event EINA_UNUSED)
{
Ecore_Wl2_Event_Seat_Selection *sel = event;
Ecore_Evas *ee = data;
Ecore_Wl2_Input *input;
unsigned int seat = sel->seat;
_reeval_seat(&seat, ee);
input = _fetch_input(ee, seat);
if (!ecore_wl2_dnd_selection_get(input))
return ECORE_CALLBACK_PASS_ON;
if (ee->func.fn_selection_changed)
ee->func.fn_selection_changed(ee, 0, ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER);
return ECORE_CALLBACK_PASS_ON;
}
typedef struct {
Eina_Rw_Slice slice;
unsigned int written_bytes;
} Delayed_Writing;
static Eina_Bool
_write_to_fd(void *data, Ecore_Fd_Handler *fd_handler)
{
int fd = ecore_main_fd_handler_fd_get(fd_handler);
Delayed_Writing *slice = data;
size_t len = write(fd, slice->slice.mem + slice->written_bytes, slice->slice.len - slice->written_bytes);
slice->written_bytes += len;
if (slice->written_bytes != slice->slice.len)
{
return EINA_TRUE;
}
else
{
ecore_main_fd_handler_del(fd_handler);
free(slice->slice.mem);
free(slice);
close(fd);
return EINA_FALSE;
}
}
static Eina_Bool
_wl_interaction_send(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Wl2_Event_Data_Source_Send *ev = event;
Ecore_Evas *ee = data;
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Ecore_Evas_Wl_Selection_Data *selection = NULL;
Delayed_Writing *forign_slice = calloc(1, sizeof(Delayed_Writing));
Ecore_Evas_Selection_Buffer buffer = ECORE_EVAS_SELECTION_BUFFER_LAST;
if (ev->serial == wdata->selection_data[ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER].sent_serial)
buffer = ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER;
else if (ev->serial == wdata->selection_data[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER].sent_serial)
{
buffer = ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER;
ee->drag.accepted = EINA_TRUE;
}
if (buffer == ECORE_EVAS_SELECTION_BUFFER_LAST)
{
//silent return, this send request was *not* for this window
return ECORE_CALLBACK_PASS_ON;
}
selection = &wdata->selection_data[buffer];
EINA_SAFETY_ON_NULL_GOTO(selection, end);
EINA_SAFETY_ON_NULL_GOTO(selection->callbacks.delivery, end);
EINA_SAFETY_ON_FALSE_GOTO(selection->callbacks.delivery(ee, ev->seat, buffer, ev->type, &forign_slice->slice), end);
ecore_main_fd_handler_add(ev->fd, ECORE_FD_WRITE, _write_to_fd, forign_slice, NULL, NULL);
end:
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_receive(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Ecore_Wl2_Event_Offer_Data_Ready *ready = event;
Ecore_Evas_Selection_Buffer selection = ECORE_EVAS_SELECTION_BUFFER_LAST;
for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i)
{
if (wdata->selection_data[i].offer == ready->offer)
{
selection = i;
break;
}
}
if (selection == ECORE_EVAS_SELECTION_BUFFER_LAST)
return ECORE_CALLBACK_PASS_ON;
//Now deliver the content
Eina_Slice slice;
if (!strncmp(ready->mimetype, "text", strlen("text")))
{
//ensure that we always have a \0 at the end, there is no assertion that \0 is included here.
slice.len = ready->len + 1;
slice.mem = eina_memdup((unsigned char*)ready->data, ready->len, EINA_TRUE);
}
else
{
slice.len = ready->len;
slice.mem = ready->data;
}
Eina_Content *content = eina_content_new(slice, ready->mimetype);
if (wdata->selection_data[selection].later_convert)
{
Eina_Content *tmp = eina_content_convert(content, wdata->selection_data[selection].later_convert);
wdata->selection_data[selection].later_convert = NULL;
eina_content_free(content);
content = tmp;
}
eina_promise_resolve(wdata->selection_data[selection].delivery, eina_value_content_init(content));
wdata->selection_data[selection].delivery = NULL;
eina_content_free(content);
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_dnd_leave(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Eina_Position2D cpos;
Eina_Position2D fpos = EINA_POSITION2D(0, 0);
Ecore_Wl2_Event_Dnd_Leave *ev = event;
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
if (ee->prop.window != (Ecore_Window)ev->win) return ECORE_CALLBACK_PASS_ON;
//evas_output_framespace_get(ee->evas, &fpos.x, &fpos.y, NULL, NULL);
ecore_wl2_input_pointer_xy_get(ecore_wl2_display_input_find(ev->display, ev->seat), &cpos.x, &cpos.y);
ecore_evas_dnd_leave(data, ev->seat, EINA_POSITION2D(cpos.x - fpos.x, cpos.y - fpos.y));
wdata->external_offer = NULL;
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_dnd_motion(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Ecore_Wl2_Event_Dnd_Motion *ev = event;
Eina_Position2D fpos = EINA_POSITION2D(0, 0);
if (ee->prop.window != (Ecore_Window)ev->win) return ECORE_CALLBACK_PASS_ON;
evas_output_framespace_get(ee->evas, &fpos.x, &fpos.y, NULL, NULL);
ecore_evas_dnd_position_set(data, ev->seat, EINA_POSITION2D(ev->x - fpos.x, ev->y - fpos.y));
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_dnd_enter(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Ecore_Wl2_Event_Dnd_Enter *ev = event;
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
Eina_Position2D fpos = EINA_POSITION2D(0, 0);
if (ee->prop.window != (Ecore_Window)ev->win) return ECORE_CALLBACK_PASS_ON;
evas_output_framespace_get(ee->evas, &fpos.x, &fpos.y, NULL, NULL);
ecore_evas_dnd_enter(data, ev->seat, eina_array_iterator_new(ecore_wl2_offer_mimes_get(ev->offer)), EINA_POSITION2D(ev->x - fpos.x, ev->y - fpos.y));
ecore_wl2_offer_mimes_set(ev->offer, ecore_wl2_offer_mimes_get(ev->offer));
wdata->external_offer = ev->offer;
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_dnd_drop(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Ecore_Wl2_Event_Dnd_Drop *ev = event;
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
if (ee->prop.window != (Ecore_Window)ev->win) return ECORE_CALLBACK_PASS_ON;
wdata = ee->engine.data;
if (ee->func.fn_dnd_drop)
ee->func.fn_dnd_drop(ee, ev->seat, ecore_evas_dnd_pos_get(ee, ev->seat), "ask");
ecore_wl2_dnd_drag_end(_fetch_input(ee, ev->seat));
wdata->external_offer = NULL;
return ECORE_CALLBACK_PASS_ON;
}
static Eina_Bool
_wl_selection_dnd_end(void *data, int type EINA_UNUSED, void *event)
{
Ecore_Evas *ee = data;
Ecore_Wl2_Event_Dnd_End *ev = event;
if (ee->prop.window != (Ecore_Window)ev->win) return ECORE_CALLBACK_PASS_ON;
if (ee->drag.free)
ee->drag.free(ee, ev->seat, ee->drag.data, ee->drag.accepted);
ee->drag.free = NULL;
//we got dropped, we should call
return ECORE_CALLBACK_PASS_ON;
}
static void
_ecore_evas_wl_selection_init(Ecore_Evas *ee)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
wdata->changed_handler = ecore_event_handler_add(ECORE_WL2_EVENT_SEAT_SELECTION,
_wl_selection_changed, ee);
wdata->send_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_SEND,
_wl_interaction_send, ee);
wdata->offer_handler = ecore_event_handler_add(ECORE_WL2_EVENT_OFFER_DATA_READY,
_wl_selection_receive, ee);
wdata->dnd_leave_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_LEAVE,
_wl_selection_dnd_leave, ee);
wdata->dnd_motion_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_MOTION,
_wl_selection_dnd_motion, ee);
wdata->dnd_enter_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_ENTER,
_wl_selection_dnd_enter, ee);
wdata->dnd_drop_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DND_DROP,
_wl_selection_dnd_drop, ee);
wdata->dnd_end_handler = ecore_event_handler_add(ECORE_WL2_EVENT_DATA_SOURCE_DROP,
_wl_selection_dnd_end, ee);
for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i)
{
wdata->selection_data[i].callbacks.available_types = NULL;
wdata->selection_data[i].callbacks.delivery = NULL;
wdata->selection_data[i].callbacks.cancel = NULL;
}
}
static Eina_Bool
_ecore_evas_wl_dnd_start(Ecore_Evas *ee, unsigned int seat, Eina_Array *available_types, Ecore_Evas *drag_rep, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel, const char *action EINA_UNUSED)
{
Ecore_Evas_Engine_Wl_Data *wdata = ee->engine.data;
const char *tmp_array[eina_array_count(available_types) + 1];
_reeval_seat(&seat, ee);
_store_selection_cbs(ee, seat, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER, available_types, delivery, cancel);
for (unsigned int i = 0; i < eina_array_count(available_types); ++i)
{
tmp_array[i] = eina_array_data_get(available_types, i);
}
tmp_array[eina_array_count(available_types)] = NULL;
ecore_wl2_dnd_drag_types_set(_fetch_input(ee, seat), (const char**)tmp_array);
wdata->selection_data[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER].sent_serial =
ecore_wl2_dnd_drag_start(_fetch_input(ee, seat), _ecore_evas_wayland_window_get(ee), _ecore_evas_wayland_window_get(drag_rep));
return EINA_TRUE;
}
static Eina_Bool
_ecore_evas_wl_dnd_stop(Ecore_Evas *ee, unsigned int seat)
{
_clear_selection(ee, seat, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER);
_reeval_seat(&seat, ee);
ecore_wl2_dnd_drag_end(_fetch_input(ee, seat));
return EINA_TRUE;
}
static Ecore_Evas_Engine_Func _ecore_wl_engine_func =
{
_ecore_evas_wl_common_free,
@ -2474,9 +2936,11 @@ static Ecore_Evas_Engine_Func _ecore_wl_engine_func =
_ecore_evas_wl_common_pointer_device_xy_get,
_ecore_evas_wl_common_prepare,
NULL, //fn_last_tick_get
NULL, //fn_selection_claim
NULL, //fn_selection_has_owner
NULL, //fn_selection_request
_ecore_evas_wl_selection_claim, //fn_selection_claim
_ecore_evas_wl_selection_has_owner, //fn_selection_has_owner
_ecore_evas_wl_selection_request, //fn_selection_request
_ecore_evas_wl_dnd_start, //fn_dnd_start
_ecore_evas_wl_dnd_stop, //fn_dnd_stop
};
static void
@ -2653,6 +3117,7 @@ _ecore_evas_wl_common_new_internal(const char *disp_name, Ecore_Window parent, i
}
_ecore_evas_wl_common_wm_rotation_protocol_set(ee);
_ecore_evas_wl_selection_init(ee);
ecore_evas_done(ee, EINA_FALSE);

View File

@ -34,12 +34,23 @@
typedef struct _Ecore_Evas_Engine_Wl_Data Ecore_Evas_Engine_Wl_Data;
typedef struct _Ecore_Evas_Wl_Selection_Data Ecore_Evas_Wl_Selection_Data;
struct _Ecore_Evas_Wl_Selection_Data
{
Ecore_Evas_Selection_Callbacks callbacks;
Eina_Promise *delivery;
Ecore_Wl2_Offer *offer;
const char *later_convert;
uint32_t sent_serial; //The serial of the last sent selection op
};
struct _Ecore_Evas_Engine_Wl_Data
{
Ecore_Wl2_Display *display;
Eina_List *regen_objs;
Ecore_Wl2_Window *parent, *win;
Ecore_Event_Handler *sync_handler;
Ecore_Event_Handler *sync_handler, *changed_handler, *end_handler, *send_handler, *offer_handler, *dnd_leave_handler, *dnd_motion_handler, *dnd_enter_handler, *dnd_drop_handler, *dnd_end_handler;
int fx, fy, fw, fh;
Ecore_Wl2_Frame_Cb_Handle *frame;
int x_rel;
@ -47,7 +58,8 @@ struct _Ecore_Evas_Engine_Wl_Data
uint32_t timestamp;
Eina_List *devices_list;
int cw, ch;
Ecore_Evas_Wl_Selection_Data selection_data[ECORE_EVAS_SELECTION_BUFFER_LAST];
Ecore_Wl2_Offer *external_offer;
struct
{
Eina_Bool supported : 1;