ecore_evas: introduce initial selection & dnd support for x.
authorMarcel Hollerbach <mail@marcel-hollerbach.de>
Sun, 12 Jan 2020 16:38:05 +0000 (17:38 +0100)
committerTaehyub Kim <taehyub.kim@samsung.com>
Tue, 10 Mar 2020 10:55:21 +0000 (19:55 +0900)
Seats are not implemented, if there is a type mismatch promises are
going to be rejected. Most of this code is copied over from
selection_manager.

Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com>
Differential Revision: https://phab.enlightenment.org/D11195

src/modules/ecore_evas/engines/x/ecore_evas_x.c

index 0be67945a940cecc8c6e1c99fd6fd6d7c79e403a..46c237a78d729d4590919a30619f4168bcff4084 100644 (file)
@@ -73,6 +73,16 @@ static Eina_Bool wm_exists;
 
 typedef struct _Ecore_Evas_Engine_Data_X11 Ecore_Evas_Engine_Data_X11;
 
+typedef struct {
+   Ecore_Evas_Selection_Callbacks callbacks;
+   Ecore_Evas_Selection_Buffer buffer;
+   Ecore_Evas *ee;
+   Eina_Promise *delivery;
+   Eina_Array *acceptable_type;
+   Eina_Stringshare *requested_type;
+   Eina_Stringshare *later_conversion;
+} Ecore_Evas_X11_Selection_Data;
+
 struct _Ecore_Evas_Engine_Data_X11 {
    Ecore_X_Window win_root;
    Eina_List     *win_extra;
@@ -128,6 +138,11 @@ struct _Ecore_Evas_Engine_Data_X11 {
         void *visual; // store visual used to create pixmap
         unsigned long colormap; // store colormap used to create pixmap
      } pixmap;
+   Ecore_Evas_X11_Selection_Data selection_data[ECORE_EVAS_SELECTION_BUFFER_LAST];
+   Eina_Array *xserver_atom_name_during_dnd;
+   Ecore_Event_Handler *mouse_up_handler;
+   Ecore_Job *init_job;
+   int skip_clean_event;
    Eina_Bool destroyed : 1; // X window has been deleted and cannot be used
    Eina_Bool fully_obscured : 1; // X window is fully obscured
    Eina_Bool configured : 1; // X window has been configured
@@ -151,6 +166,8 @@ static void _alpha_do(Ecore_Evas *, int);
 static void _transparent_do(Ecore_Evas *, int);
 static void _avoid_damage_do(Ecore_Evas *, int);
 static void _rotation_do(Ecore_Evas *, int, int);
+static void _ecore_evas_x_selection_init(void);
+static void _ecore_evas_x_selection_window_init(Ecore_Evas *ee);
 
 #define SWAP_INT(a, b) do { a ^= b; b ^= a; a ^= b; } while (0)
 
@@ -1962,6 +1979,7 @@ _ecore_evas_x_init(void)
      ecore_event_handler_add(ECORE_X_EVENT_WINDOW_CREATE,
                              _ecore_evas_x_event_window_create, NULL);
    ecore_event_evas_init();
+   _ecore_evas_x_selection_init();
    return _ecore_evas_init_count;
 }
 
@@ -1989,6 +2007,7 @@ _ecore_evas_x_free(Ecore_Evas *ee)
 {
    Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data;
 
+   ecore_job_del(edata->init_job);
    if (edata->pixmap.back)
      ecore_x_pixmap_free(edata->pixmap.back);
    if (edata->pixmap.front)
@@ -2688,6 +2707,7 @@ _alpha_do(Ecore_Evas *ee, int alpha)
    _ecore_evas_x_aux_hints_supported_update(ee);
    _ecore_evas_x_aux_hints_update(ee);
    _ecore_evas_x_size_pos_hints_update(ee);
+   _ecore_evas_x_selection_window_init(ee);
 #endif /* BUILD_ECORE_EVAS_SOFTWARE_X11 */
    if ((id = getenv("DESKTOP_STARTUP_ID")))
      {
@@ -2842,6 +2862,7 @@ _ecore_evas_x_alpha_set(Ecore_Evas *ee, int alpha)
         _ecore_evas_x_aux_hints_supported_update(ee);
         _ecore_evas_x_aux_hints_update(ee);
         _ecore_evas_x_size_pos_hints_update(ee);
+        _ecore_evas_x_selection_window_init(ee);
 #endif /* BUILD_ECORE_EVAS_OPENGL_X11 */
         if ((id = getenv("DESKTOP_STARTUP_ID")))
           {
@@ -3659,6 +3680,731 @@ _ecore_evas_x_aux_hints_set(Ecore_Evas *ee, const char *hints)
        (ee->prop.window, ECORE_X_ATOM_E_WINDOW_AUX_HINT);
 }
 
+static Ecore_X_Atom ecore_evas_selection_to_atom[] = {0, 0, 0, 0};
+static Ecore_Event_Handler *ecore_evas_selection_handlers[8];
+
+static inline Ecore_Evas_Selection_Buffer
+_atom_to_selection(Ecore_X_Atom atom)
+{
+   for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i)
+     {
+        if (ecore_evas_selection_to_atom[i] == atom)
+          return i;
+     }
+   return ECORE_EVAS_SELECTION_BUFFER_LAST;
+}
+
+static Eina_Stringshare*
+_decrypt_type(const char *target)
+{
+   // reference https://tronche.com/gui/x/icccm/sec-2.html
+   if (eina_streq(target, "TEXT")) return eina_stringshare_add("text/plain");
+    //FIXME no support in eina_content for that so far
+   if (eina_streq(target, "COMPOUND_TEXT")) return eina_stringshare_add("text/plain");
+    // reference https://tronche.com/gui/x/icccm/sec-2.html
+   if (eina_streq(target, "STRING")) return eina_stringshare_add("text/plain;charset=iso-8859-1");
+   if (eina_streq(target, "UTF8_STRING")) return eina_stringshare_add("text/plain;charset=utf-8");
+
+   return eina_stringshare_add(target);
+}
+
+static Eina_Stringshare*
+_mime_to_xserver_type(const char *target)
+{
+   // FIXME // reference https://tronche.com/gui/x/icccm/sec-2.html says it is in the owners choice of encoding, not sure what this means directly here
+   if (eina_streq(target, "text/plain")) return eina_stringshare_add("TEXT");
+    // reference https://tronche.com/gui/x/icccm/sec-2.html
+   if (eina_streq(target, "text/plain;charset=iso-8859-1")) return eina_stringshare_add("STRING");
+   if (eina_streq(target, "text/plain;charset=utf-8")) return eina_stringshare_add("UTF8_STRING");
+
+   return eina_stringshare_add(target);
+}
+
+static inline void
+_clear_selection(Ecore_Evas *ee, Ecore_Evas_Selection_Buffer selection)
+{
+   Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data;
+   Ecore_Evas_Selection_Callbacks *cbs = &edata->selection_data[selection].callbacks;
+
+   EINA_SAFETY_ON_FALSE_RETURN(cbs->cancel);
+
+   cbs->cancel(ee, 1, selection);
+   eina_array_free(cbs->available_types);
+
+   cbs->delivery = NULL;
+   cbs->cancel = NULL;
+   cbs->available_types = NULL;
+}
+
+static void
+_clear_selection_delivery(Ecore_Evas *ee, Ecore_Evas_Selection_Buffer selection)
+{
+   Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data;
+   eina_stringshare_replace(&edata->selection_data[selection].requested_type, NULL);
+   eina_stringshare_replace(&edata->selection_data[selection].later_conversion, NULL);
+   edata->selection_data[selection].delivery = NULL;
+   eina_array_free(edata->selection_data[selection].acceptable_type);
+   edata->selection_data[selection].acceptable_type = NULL;
+}
+
+static void
+_ecore_x_selection_request(Ecore_X_Window win, Ecore_Evas_Selection_Buffer selection, const char *type)
+{
+   if (selection == ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER)
+     ecore_x_selection_primary_request(win, type);
+   else if (selection == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER)
+     ecore_x_selection_clipboard_request(win, type);
+   else
+     ecore_x_selection_xdnd_request(win, type);
+}
+
+static void
+_search_fitting_type(Ecore_Evas *ee, Ecore_Evas_Engine_Data_X11 *edata, Ecore_Evas_Selection_Buffer selection, Eina_Array *arr)
+{
+   Eina_Stringshare *mime_type;
+   Eina_Bool found_conversion = EINA_FALSE;
+
+#define HANDLE_TYPE() \
+   { \
+      edata->selection_data[selection].requested_type = eina_stringshare_add(x11_name); \
+      edata->selection_data[selection].later_conversion = eina_stringshare_add(acceptable_type);\
+      found_conversion = EINA_TRUE; \
+      break; \
+   }
+
+   EINA_SAFETY_ON_NULL_RETURN(edata->selection_data[selection].acceptable_type);
+
+   for (unsigned int i = 0; i < eina_array_count(arr) && !found_conversion; ++i)
+     {
+        const char *x11_name = eina_array_data_get(arr, i);
+        mime_type = _decrypt_type(x11_name);
+
+        for (unsigned int j = 0; j < eina_array_count(edata->selection_data[selection].acceptable_type) && !found_conversion; ++j)
+          {
+             const char *acceptable_type = (const char*) eina_array_data_get(edata->selection_data[selection].acceptable_type, j);
+
+             if (mime_type == acceptable_type)
+               HANDLE_TYPE()
+
+             //if there is no available type yet, check if we can convert to the desiared type via this type
+             if (!found_conversion)
+               {
+                  const char *convertion_type = NULL;
+                  Eina_Iterator *iter = eina_content_converter_possible_conversions(mime_type);
+                  EINA_ITERATOR_FOREACH(iter, convertion_type)
+                    {
+                       if (convertion_type == acceptable_type)
+                         HANDLE_TYPE()
+                    }
+                  eina_iterator_free(iter);
+               }
+          }
+        eina_stringshare_del(mime_type);
+     }
+   if (found_conversion)
+     {
+        _ecore_x_selection_request(ee->prop.window, selection, edata->selection_data[selection].requested_type);
+     }
+   else
+     {
+        eina_promise_resolve(edata->selection_data[selection].delivery, eina_value_error_init(ecore_evas_no_matching_type));
+        _clear_selection_delivery(ee, selection);
+     }
+
+#undef HANDLE_TYPE
+}
+
+static void
+_search_fitting_type_from_event(Ecore_Evas *ee, Ecore_Evas_Engine_Data_X11 *edata, Ecore_Evas_Selection_Buffer selection, Ecore_X_Event_Selection_Notify *ev)
+{
+   Ecore_X_Atom *available_atoms;
+   Ecore_X_Selection_Data_Targets *targets;
+   Eina_Array *tmp = eina_array_new(10);
+
+   targets = ev->data;
+   available_atoms = (Ecore_X_Atom *)targets->data.data;
+   for (int i = 0; i < targets->data.length; ++i)
+     {
+        Ecore_X_Atom atom = available_atoms[i];
+        eina_array_push(tmp, ecore_x_atom_name_get(atom));
+     }
+   _search_fitting_type(ee, edata, selection, tmp);
+   eina_array_free(tmp);
+}
+
+static void
+_deliver_content(Ecore_Evas *ee, Ecore_Evas_Engine_Data_X11 *edata, Ecore_Evas_Selection_Buffer selection, Ecore_X_Event_Selection_Notify *ev)
+{
+   Ecore_X_Selection_Data *x11_data = ev->data;
+   Eina_Rw_Slice data;
+   Eina_Content *result;
+   Eina_Stringshare *mime_type = _decrypt_type(edata->selection_data[selection].requested_type);
+
+   if (!strncmp(mime_type, "text", strlen("text")))
+     {
+        //ensure that we always have a \0 at the end, there is no assertion that \0 is included here.
+        data.len = x11_data->length + 1;
+        data.mem = eina_memdup(x11_data->data, x11_data->length, EINA_TRUE);
+     }
+   else
+     {
+        data.len = x11_data->length;
+        data.mem = x11_data->data;
+     }
+
+   result = eina_content_new(eina_rw_slice_slice_get(data), mime_type);
+
+   //ensure that we deliver the correct type, we might have choosen a convertion before
+   if (edata->selection_data[selection].later_conversion != mime_type)
+     {
+        Eina_Content *tmp = eina_content_convert(result, edata->selection_data[selection].later_conversion);
+        eina_content_free(result);
+        result = tmp;
+     }
+
+   eina_promise_resolve(edata->selection_data[selection].delivery, eina_value_content_init(result));
+   eina_content_free(result);
+   _clear_selection_delivery(ee, selection);
+
+   if (selection == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER)
+     ecore_x_dnd_send_finished();
+}
+
+static Eina_Bool
+_ecore_evas_x_selection_notify(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Selection_Notify *ev = event;
+   Ecore_Evas_Selection_Buffer selection;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(ev->win);
+   selection = _atom_to_selection(ev->atom);
+   EINA_SAFETY_ON_FALSE_GOTO(!!ee, end);
+   EINA_SAFETY_ON_FALSE_GOTO(selection != ECORE_EVAS_SELECTION_BUFFER_LAST, end);
+   edata = ee->engine.data;
+
+   //if dnd drops above us, and even if we did not request anything, we are getting notified, refuse to do anything
+   if (selection == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER &&
+       !edata->selection_data[selection].later_conversion)
+     {
+        ecore_x_dnd_send_finished();
+     }
+   else
+     {
+        if (eina_streq(ev->target, "TARGETS") || eina_streq(ev->target, "ATOMS"))
+          {
+             //This will decide for a type, and will sent that via _ecore_x_selection_request
+             EINA_SAFETY_ON_FALSE_RETURN_VAL(!edata->selection_data[selection].later_conversion, EINA_FALSE);
+             EINA_SAFETY_ON_FALSE_RETURN_VAL(!edata->selection_data[selection].requested_type, EINA_FALSE);
+             _search_fitting_type_from_event(ee, edata, selection, ev);
+          }
+        else
+          {
+             //This will read the data, fill it into a Eina_Content apply all conversions required.
+             EINA_SAFETY_ON_FALSE_RETURN_VAL(edata->selection_data[selection].later_conversion, EINA_FALSE);
+             EINA_SAFETY_ON_FALSE_RETURN_VAL(edata->selection_data[selection].requested_type, EINA_FALSE);
+             _deliver_content(ee, edata, selection, ev);
+          }
+     }
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ecore_evas_x_selection_clear(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Selection_Clear *ev = event;
+   Ecore_Evas_Selection_Callbacks *cbs;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas_Selection_Buffer selection;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(ev->win);
+   selection = _atom_to_selection(ev->atom);
+   EINA_SAFETY_ON_FALSE_GOTO(ee, end);
+   EINA_SAFETY_ON_FALSE_GOTO(selection != ECORE_EVAS_SELECTION_BUFFER_LAST, end);
+   edata = ee->engine.data;
+   cbs = &edata->selection_data[selection].callbacks;
+
+   //skip clean event
+   if (edata->skip_clean_event)
+     {
+        edata->skip_clean_event --;
+        goto end;
+     }
+
+   if (cbs->cancel)
+     _clear_selection(ee, selection);
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_force_stop_self_dnd(Ecore_Evas *ee)
+{
+   Ecore_Evas_Engine_Data_X11 *edata;
+
+   EINA_SAFETY_ON_NULL_RETURN(ee);
+   edata = ee->engine.data;
+   EINA_SAFETY_ON_NULL_RETURN(edata);
+
+   //Never clear the buffer for selection here.
+   //Selection buffer is freed as a response to the FINISHED event.
+   ecore_x_pointer_ungrab();
+   ecore_x_dnd_self_drop();
+   ecore_x_dnd_aware_set(ee->prop.window, EINA_FALSE);
+   ecore_event_handler_del(edata->mouse_up_handler);
+   edata->mouse_up_handler = NULL;
+
+   if (ee->drag.free)
+     ee->drag.free(ee, 1, ee->drag.data, ee->drag.accepted);
+   ee->drag.free = NULL;
+}
+
+static Eina_Bool
+_ecore_evas_x_selection_fixes_notify(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Fixes_Selection_Notify *ev = event;
+   Ecore_Evas *ee;
+   Ecore_Evas_Selection_Buffer selection;
+
+   ee = ecore_event_window_match(ev->win);
+   selection = _atom_to_selection(ev->atom);
+   EINA_SAFETY_ON_FALSE_GOTO(!!ee, end);
+   EINA_SAFETY_ON_FALSE_GOTO(selection != ECORE_EVAS_SELECTION_BUFFER_LAST, end);
+
+   //notify that the selection has changed on this ecore evas
+   if (ee->func.fn_selection_changed)
+     ee->func.fn_selection_changed(ee, 0, selection);
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_eina_content_converter(char *target, void *data, int size EINA_UNUSED, void **data_ret, int *size_ret, Ecore_X_Atom *ttype, int *typesize)
+{
+   Ecore_Evas_X11_Selection_Data *sdata = data;
+   Eina_Bool ret = EINA_FALSE;;
+   if (eina_streq(target, "TARGETS") || eina_streq(target, "ATOM"))
+     {
+        //list all available types that we have currently
+        Ecore_X_Atom *result = calloc(eina_array_count(sdata->callbacks.available_types), sizeof(Ecore_X_Atom));
+        for (unsigned int i = 0; i < eina_array_count(sdata->callbacks.available_types); ++i)
+          {
+             result[i] = ecore_x_atom_get(eina_array_data_get(sdata->callbacks.available_types, i));
+          }
+        *size_ret = eina_array_count(sdata->callbacks.available_types);
+        *data_ret = result;
+        *ttype = ECORE_X_ATOM_ATOM;
+        *typesize = 32; /* urk */
+        ret = EINA_TRUE;
+     }
+   else
+     {
+        const char *mime_type = _decrypt_type(target);
+        for (unsigned int i = 0; i < eina_array_count(sdata->callbacks.available_types); ++i)
+          {
+             if (mime_type == eina_array_data_get(sdata->callbacks.available_types, i))
+               {
+                  Eina_Rw_Slice slice;
+                  sdata->callbacks.delivery(sdata->ee, 1, sdata->buffer, mime_type, &slice);
+                  *size_ret = slice.len;
+                  *data_ret = slice.mem;
+                  *ttype = ecore_x_atom_get(target); //use here target in order to get the correct atom
+                  //FIXME in selection manager we never set here the typesize, isn't that weird ?
+                  ret = EINA_TRUE;
+                  break;
+               }
+          }
+        eina_stringshare_del(mime_type);
+     }
+
+   return ret;
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_enter(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Xdnd_Enter *enter = event;
+   Eina_Array *mime_tmp;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas *ee;
+
+   mime_tmp = eina_array_new(10);
+   ee = ecore_event_window_match(enter->win);
+   EINA_SAFETY_ON_NULL_GOTO(ee, end);
+   edata = ee->engine.data;
+   edata->xserver_atom_name_during_dnd = eina_array_new(10);
+   for (int i = 0; i < enter->num_types; ++i)
+     {
+        const char *mime_type = _decrypt_type(enter->types[i]);
+        eina_array_push(mime_tmp, mime_type);
+        eina_array_push(edata->xserver_atom_name_during_dnd, eina_stringshare_add(enter->types[i]));
+     }
+   ecore_evas_dnd_enter(ee, 1, eina_array_iterator_new(mime_tmp), EINA_POSITION2D(0,0)); //FIXME
+   eina_array_free(mime_tmp);
+
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_leave(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Xdnd_Leave *leave = event;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(leave->win);
+   EINA_SAFETY_ON_NULL_GOTO(ee, end);
+   edata = ee->engine.data;
+   ecore_evas_dnd_leave(ee, 1, EINA_POSITION2D(0,0));
+   for (unsigned int i = 0; i < eina_array_count(edata->xserver_atom_name_during_dnd); ++i)
+     {
+        eina_stringshare_del(eina_array_data_get(edata->xserver_atom_name_during_dnd, i));
+     }
+   eina_array_free(edata->xserver_atom_name_during_dnd);
+   edata->xserver_atom_name_during_dnd = NULL;
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Ecore_X_Atom
+_x11_dnd_action_rev_map(const char* action)
+{
+   if (eina_streq(action, "copy")) return ECORE_X_ATOM_XDND_ACTION_COPY;
+   if (eina_streq(action, "move")) return ECORE_X_ATOM_XDND_ACTION_MOVE;
+   else if (eina_streq(action, "privat")) return ECORE_X_ATOM_XDND_ACTION_PRIVATE;
+   else if (eina_streq(action, "ask")) return ECORE_X_ATOM_XDND_ACTION_ASK;
+   else if (eina_streq(action, "list")) return ECORE_X_ATOM_XDND_ACTION_LIST;
+   else if (eina_streq(action, "link")) return ECORE_X_ATOM_XDND_ACTION_LINK;
+   else if (eina_streq(action, "description")) return ECORE_X_ATOM_XDND_ACTION_DESCRIPTION;
+   return 0;
+}
+
+static const char*
+_x11_dnd_action_map(Ecore_X_Atom action)
+{
+   if (action == ECORE_X_DND_ACTION_COPY) return "copy";
+   if (action == ECORE_X_ATOM_XDND_ACTION_MOVE) return "move";
+   if (action == ECORE_X_ATOM_XDND_ACTION_PRIVATE) return "privat";
+   if (action == ECORE_X_ATOM_XDND_ACTION_ASK) return "ask";
+   if (action == ECORE_X_ATOM_XDND_ACTION_LIST) return "list";
+   if (action == ECORE_X_ATOM_XDND_ACTION_LINK) return "link";
+   if (action == ECORE_X_ATOM_XDND_ACTION_DESCRIPTION) return "description";
+
+   return "unknown";
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_position(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Xdnd_Position *pos = event;
+   int x, y, w, h;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(pos->win);
+   EINA_SAFETY_ON_NULL_GOTO(ee, end);
+   ecore_evas_geometry_get(ee, &x, &y, &w, &h);
+   ecore_evas_dnd_position_set(ee, 1, EINA_POSITION2D(pos->position.x - x, pos->position.y - y));
+   ecore_x_dnd_send_status(EINA_TRUE, EINA_FALSE, (Ecore_X_Rectangle){x,y,w,h}, pos->action);
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_drop(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Xdnd_Drop *drop = event;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(drop->win);
+   EINA_SAFETY_ON_NULL_GOTO(ee, end);
+   edata = ee->engine.data;
+   if (ee->func.fn_dnd_drop)
+     ee->func.fn_dnd_drop(ee, 1, ecore_evas_dnd_pos_get(ee, 1), _x11_dnd_action_map(drop->action));
+   if (!edata->selection_data[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER].requested_type)
+     {
+        ecore_x_dnd_send_finished();
+     }
+   ecore_evas_dnd_leave(ee, 1, EINA_POSITION2D(drop->position.x ,drop->position.y));
+   eina_array_free(edata->xserver_atom_name_during_dnd);
+   edata->xserver_atom_name_during_dnd = NULL;
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static Eina_Bool
+_ecore_evas_x_finished(void *udata EINA_UNUSED, int type EINA_UNUSED, void *event)
+{
+   Ecore_X_Event_Xdnd_Finished *finished = event;
+   Ecore_Evas *ee;
+
+   ee = ecore_event_window_match(finished->win);
+   EINA_SAFETY_ON_NULL_GOTO(ee, end);
+
+   _clear_selection(ee, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER);
+end:
+   return ECORE_CALLBACK_PASS_ON;
+}
+
+static void
+_ecore_evas_x_selection_init(void)
+{
+   Ecore_X_Atom _ecore_evas_selection_to_atom[] = {ECORE_X_ATOM_SELECTION_PRIMARY, ECORE_X_ATOM_SELECTION_CLIPBOARD, ECORE_X_ATOM_SELECTION_XDND};
+
+   for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i)
+     {
+        ecore_evas_selection_to_atom[i] = _ecore_evas_selection_to_atom[i];
+     }
+
+   ecore_evas_selection_handlers[0] =
+     ecore_event_handler_add(ECORE_X_EVENT_SELECTION_NOTIFY,
+                                                     _ecore_evas_x_selection_notify, NULL);
+   ecore_evas_selection_handlers[1] =
+     ecore_event_handler_add(ECORE_X_EVENT_SELECTION_CLEAR,
+                                                    _ecore_evas_x_selection_clear, NULL);
+   if (ECORE_X_EVENT_FIXES_SELECTION_NOTIFY)
+     ecore_evas_selection_handlers[2] =
+       ecore_event_handler_add(ECORE_X_EVENT_FIXES_SELECTION_NOTIFY,
+                                                    _ecore_evas_x_selection_fixes_notify, NULL);
+
+   ecore_evas_selection_handlers[3] = ecore_event_handler_add(ECORE_X_EVENT_XDND_ENTER,
+                                                              _ecore_evas_x_dnd_enter, NULL);
+   ecore_evas_selection_handlers[4] = ecore_event_handler_add(ECORE_X_EVENT_XDND_LEAVE,
+                                                              _ecore_evas_x_dnd_leave, NULL);
+   ecore_evas_selection_handlers[5] = ecore_event_handler_add(ECORE_X_EVENT_XDND_POSITION,
+                                                              _ecore_evas_x_dnd_position, NULL);
+   ecore_evas_selection_handlers[6] = ecore_event_handler_add(ECORE_X_EVENT_XDND_DROP,
+                                                              _ecore_evas_x_dnd_drop, NULL);
+   ecore_evas_selection_handlers[7] = ecore_event_handler_add(ECORE_X_EVENT_XDND_FINISHED,
+                                                              _ecore_evas_x_finished, NULL);
+   /* for us known type */
+   char *supported_types[] = {
+     "text/plain",
+     "text/plain;charset=utf-8",
+     "image/png",
+     "image/jpeg",
+     "image/x-ms-bmp",
+     "image/gif",
+     "image/tiff",
+     "image/svg+xml",
+     "image/x-xpixmap",
+     "image/x-tga",
+     "image/x-portable-pixmap",
+     "TEXT",
+     "COMPOUND_TEXT",
+     "STRING",
+     "UTF8_STRING",
+     "text/x-vcard",
+     "text/uri-list",
+     "application/x-elementary-markup",
+     "ATOM",
+     "TARGETS",
+     NULL
+   };
+   for (int i = 0; supported_types[i]; ++i)
+     {
+       ecore_x_selection_converter_add(supported_types[i], _eina_content_converter);
+     }
+}
+
+static Eina_Bool
+_ecore_evas_x_selection_has_owner(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection)
+{
+   return !!ecore_x_selection_owner_get(ecore_evas_selection_to_atom[selection]);
+}
+
+static void
+_deliver_selection_changed(void *data)
+{
+   Ecore_Evas *ee = data;
+   Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data;
+
+   if (!ee->func.fn_selection_changed)
+     goto end;
+
+   if (_ecore_evas_x_selection_has_owner(ee, 1, ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER))
+     ee->func.fn_selection_changed(ee, 1, ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER);
+   if (_ecore_evas_x_selection_has_owner(ee, 1, ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER))
+     ee->func.fn_selection_changed(ee, 1, ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER);
+   if (_ecore_evas_x_selection_has_owner(ee, 1, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER))
+     ee->func.fn_selection_changed(ee, 1, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER);
+end:
+    edata->init_job = NULL;
+}
+
+static void
+_ecore_evas_x_selection_window_init(Ecore_Evas *ee)
+{
+   Ecore_Evas_Engine_Data_X11 *edata = ee->engine.data;
+   for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i)
+     {
+        ecore_x_fixes_window_selection_notification_request(ee->prop.window, ecore_evas_selection_to_atom[i]);
+        edata->selection_data[i].ee = ee;
+        edata->selection_data[i].buffer = i;
+     }
+   ecore_x_dnd_aware_set(ee->prop.window, EINA_TRUE);
+   edata->init_job = ecore_job_add(_deliver_selection_changed, ee);
+}
+
+static void
+_store_selection_cbs(Ecore_Evas *ee, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Eina_Bool (*delivery)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, const char *type, Eina_Rw_Slice *slice), void (*cancel)(Ecore_Evas *ee, unsigned int seat,  Ecore_Evas_Selection_Buffer buffer))
+{
+   Ecore_Evas_X11_Selection_Data *sdata;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_Evas_Selection_Callbacks *cbs;
+
+   edata = ee->engine.data;
+   sdata = &edata->selection_data[selection];
+   cbs = &sdata->callbacks;
+
+   if (cbs->cancel)
+     {
+        _clear_selection(ee, selection);
+        edata->skip_clean_event ++; //we are going to overwrite our own selection, this will emit a clean event, but we already freed it.
+     }
+
+   cbs->delivery = delivery;
+   cbs->cancel = cancel;
+   cbs->available_types = available_types;
+}
+
+static Eina_Bool
+_ecore_evas_x_selection_claim(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel)
+{
+   Ecore_Evas_X11_Selection_Data *sdata;
+   Ecore_Evas_Engine_Data_X11 *edata;
+
+   edata = ee->engine.data;
+   sdata = &edata->selection_data[selection];
+
+   _store_selection_cbs(ee, selection, available_types, delivery, cancel);
+
+   if (eina_array_count(available_types) > 0)
+     {
+        //the commands below will *copy* the content of sdata, this you have to ensure that clear is called when sdata is changed.
+        if (selection == ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER)
+          ecore_x_selection_primary_set(ee->prop.window, sdata, sizeof(Ecore_Evas_X11_Selection_Data));
+        else if (selection == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER)
+          ecore_x_selection_clipboard_set(ee->prop.window, sdata, sizeof(Ecore_Evas_X11_Selection_Data));
+     }
+   else
+     {
+        if (selection == ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER)
+          ecore_x_selection_primary_clear();
+        else if (selection == ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER)
+          ecore_x_selection_clipboard_clear();
+     }
+
+   //for drag and drop, we are not calling anything in here
+
+   return EINA_TRUE;
+}
+
+Eina_Future*
+_ecore_evas_x_selection_request(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection, Eina_Array *acceptable_type)
+{
+   Ecore_Evas_X11_Selection_Data *sdata;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Eina_Future *future;
+
+   edata = ee->engine.data;
+   sdata = &edata->selection_data[selection];
+
+   if (sdata->delivery)
+     {
+        eina_promise_reject(sdata->delivery, ecore_evas_request_replaced);
+        _clear_selection_delivery(ee, selection);
+     }
+   sdata->delivery = efl_loop_promise_new(efl_main_loop_get());
+   sdata->acceptable_type = acceptable_type;
+   future = eina_future_new(sdata->delivery);
+
+   if (selection == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER)
+     {
+        //when in dnd - we are requesting out of the set that we know from the enter event
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(!edata->selection_data[selection].later_conversion, NULL);
+        EINA_SAFETY_ON_FALSE_RETURN_VAL(!edata->selection_data[selection].requested_type, NULL);
+        _search_fitting_type(ee, edata, selection, edata->xserver_atom_name_during_dnd);
+     }
+   else
+     {
+        //when not dnd - we are first wanting to know what is available
+        _ecore_x_selection_request(ee->prop.window, selection, ECORE_X_SELECTION_TARGET_TARGETS);
+     }
+
+   return future;
+}
+
+static void
+_x11_drag_move(void *data, Ecore_X_Xdnd_Position *pos)
+{
+   Ecore_Evas *ee = data;
+   Eina_Rect rect;
+
+   ecore_evas_geometry_get(ee->drag.rep, &rect.x, &rect.y, &rect.w, &rect.h);
+
+   ecore_evas_move(ee->drag.rep, pos->position.x - rect.w / 2, pos->position.y - rect.h/2);
+}
+
+static Eina_Bool
+_x11_drag_mouse_up(void *data, int etype EINA_UNUSED, void *event EINA_UNUSED)
+{
+   Ecore_Evas *ee = data;
+
+   _force_stop_self_dnd(ee);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_start(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Array *available_types, Ecore_Evas *drag_rep, Eina_Bool (*delivery)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, const char *type, Eina_Rw_Slice *slice), void (*cancel)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer), const char* action)
+{
+   Ecore_Evas_X11_Selection_Data *sdata;
+   Ecore_Evas_Engine_Data_X11 *edata;
+   Ecore_X_Atom actx;
+
+   edata = ee->engine.data;
+   sdata = &edata->selection_data[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER];
+   _store_selection_cbs(ee, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER, available_types, delivery, cancel);
+
+   //first set all types we have
+   ecore_x_dnd_types_set(ee->prop.window, NULL, 0);
+   for (unsigned int i = 0; i < eina_array_count(available_types); ++i)
+     {
+        const char *xserver_mime_type = _mime_to_xserver_type(eina_array_data_get(available_types, i));
+        ecore_x_dnd_type_set(ee->prop.window, xserver_mime_type, EINA_TRUE);
+        eina_stringshare_del(xserver_mime_type);
+     }
+   ecore_x_dnd_aware_set(ee->prop.window, EINA_TRUE);
+   ecore_x_dnd_callback_pos_update_set(_x11_drag_move, ee);
+   ecore_x_dnd_self_begin(ee->prop.window, (unsigned char*)sdata, sizeof(Ecore_Evas_X11_Selection_Data));
+   actx = _x11_dnd_action_rev_map(action);
+   ecore_x_dnd_source_action_set(actx);
+   ecore_x_pointer_grab(ee->prop.window);
+
+   ecore_x_window_ignore_set(drag_rep->prop.window, EINA_TRUE);
+
+   if (edata->mouse_up_handler)
+      ecore_event_handler_del(edata->mouse_up_handler);
+   edata->mouse_up_handler = ecore_event_handler_add(ECORE_EVENT_MOUSE_BUTTON_UP,
+                                                    _x11_drag_mouse_up, ee);
+
+   return EINA_TRUE;
+}
+
+static Eina_Bool
+_ecore_evas_x_dnd_stop(Ecore_Evas *ee, unsigned int seat EINA_UNUSED)
+{
+   _force_stop_self_dnd(ee);
+   _clear_selection(ee, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER);
+   ecore_x_selection_xdnd_clear(); //This is needed otherwise a outdated sdata struct will be accessed
+   return EINA_TRUE;
+}
+
 static Ecore_Evas_Engine_Func _ecore_x_engine_func =
 {
    _ecore_evas_x_free,
@@ -3749,9 +4495,11 @@ static Ecore_Evas_Engine_Func _ecore_x_engine_func =
    //TIZEN_ONLY(20171218) : Add to free evas engine rsc before free evas
    NULL, //fn_evas_engine_rsc_free
 
-   NULL, //fn_selection_claim
-   NULL, //fn_selection_has_owner
-   NULL, //fn_selection_request
+   _ecore_evas_x_selection_claim, //fn_selection_claim
+   _ecore_evas_x_selection_has_owner, //fn_selection_has_owner
+   _ecore_evas_x_selection_request, //fn_selection_request
+   _ecore_evas_x_dnd_start, //fn_dnd_start
+   _ecore_evas_x_dnd_stop, //fn_dnd_stop
 };
 
 /*
@@ -4115,6 +4863,7 @@ ecore_evas_software_x11_new_internal(const char *disp_name, Ecore_X_Window paren
    _ecore_evas_x_wm_rotation_protocol_set(ee);
    _ecore_evas_x_aux_hints_supported_update(ee);
    _ecore_evas_x_aux_hints_update(ee);
+   _ecore_evas_x_selection_window_init(ee);
 
    ee->engine.func->fn_render = _ecore_evas_x_render;
    ee->draw_block = EINA_TRUE;
@@ -4551,6 +5300,7 @@ ecore_evas_gl_x11_options_new_internal(const char *disp_name, Ecore_X_Window par
    _ecore_evas_x_wm_rotation_protocol_set(ee);
    _ecore_evas_x_aux_hints_supported_update(ee);
    _ecore_evas_x_aux_hints_update(ee);
+   _ecore_evas_x_selection_window_init(ee);
 
    ee->draw_block = 1;
    if (!wm_exists) edata->configured = 1;