forked from enlightenment/efl
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. Reviewed-by: Mike Blumenkrantz <michael.blumenkrantz@gmail.com> Differential Revision: https://phab.enlightenment.org/D11195
This commit is contained in:
parent
6189c2112c
commit
e0c40abb40
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue