summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMarcel Hollerbach <mail@marcel-hollerbach.de>2020-01-12 17:38:05 +0100
committerMarcel Hollerbach <mail@marcel-hollerbach.de>2020-03-04 17:26:30 +0100
commitc3918703664103dcd8b7058b896ed3220ffeb660 (patch)
tree861deff1c7d8b2703758229b79c8caa4a95128c1
parent2951c69c49f2876eb3cc874ee3a8bab3316c3ac6 (diff)
downloadefl-c3918703664103dcd8b7058b896ed3220ffeb660.tar.gz
ecore_evas: introduce initial selection & dnd support for x.
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. Differential Revision: https://phab.enlightenment.org/D11195
-rw-r--r--src/modules/ecore_evas/engines/x/ecore_evas_x.c756
1 files changed, 753 insertions, 3 deletions
diff --git a/src/modules/ecore_evas/engines/x/ecore_evas_x.c b/src/modules/ecore_evas/engines/x/ecore_evas_x.c
index fb6eaa63f1..d689a5622c 100644
--- a/src/modules/ecore_evas/engines/x/ecore_evas_x.c
+++ b/src/modules/ecore_evas/engines/x/ecore_evas_x.c
@@ -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)
@@ -1970,6 +1987,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;
}
@@ -1997,6 +2015,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)
@@ -2696,6 +2715,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")))
{
@@ -2850,6 +2870,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")))
{
@@ -3667,6 +3688,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,
@@ -3754,9 +4500,11 @@ static Ecore_Evas_Engine_Func _ecore_x_engine_func =
NULL, //fn_pointer_device_xy_get
NULL, //fn_prepare
NULL, //fn_last_tick_get
- 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
};
/*
@@ -4120,6 +4868,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;
@@ -4556,6 +5305,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;