diff --git a/src/lib/ecore_evas/Ecore_Evas.h b/src/lib/ecore_evas/Ecore_Evas.h index d59900d788..ddc3622741 100644 --- a/src/lib/ecore_evas/Ecore_Evas.h +++ b/src/lib/ecore_evas/Ecore_Evas.h @@ -3669,6 +3669,219 @@ EAPI unsigned long ecore_evas_pixmap_colormap_get(const Ecore_Evas *ee); */ EAPI int ecore_evas_pixmap_depth_get(const Ecore_Evas *ee); +typedef enum { + ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER = 0, /**< Stores selected / highlighted selection */ + ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER = 1, /**< Stores copied things (Ctrl + C) */ + ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER = 2, /**< Stores dragged things while drag and drop is happening. */ + ECORE_EVAS_SELECTION_BUFFER_LAST = 3, +} Ecore_Evas_Selection_Buffer; + +/** + * @brief Callback called when the content of one of the selection buffers changes. + * + * @param[in] ee The Ecore_Evas that handles this selection. + * @param[in] selection The selection buffer that has changed. + */ +typedef void (*Ecore_Evas_Selection_Changed_Cb)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection); + +/** + * @brief Sets a callback for Ecore_Evas to be called when a selection buffer changes. + * + * @param[in] ee The Ecore_Evas to set the callback on. + * @param[in] cb The function to call. + * + * A call to this function will set a callback on an Ecore_Evas, causing + * @p func to be called whenever @p ee selections change. + * Only one such callback can exist for each Ecore_Evas. Calling this method multiple + * times overwrites previous functions. Use a NULL @p func to stop being notified. + * + * You will not be notified about selection changes caused by yourself. (TODO: bu5hm4n?) + * + * @warning If and when this function is called depends on the underlying + * windowing system. + */ +EAPI void ecore_evas_callback_selection_changed_set(Ecore_Evas *ee, Ecore_Evas_Selection_Changed_Cb cb); + +/** + * @brief Sets the content of the specified selection buffer. + * + * @param[in] ee The Ecore_Evas to set the selection buffer on. + * @param[in] buffer The selection buffer to set. + * @param[in] content Content to set to the selection buffer. The Eina_Content specifies the MIME type of the data. + * Ownership of the content is transferred. + * + * @note Only ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER and ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER + * buffers can be set. Drag and drop operations use a different set of methods. + */ +EAPI Eina_Bool ecore_evas_selection_set(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, Eina_Content *content); + +/** + * @brief Checks if the specified selection buffer has content. + * + * @param[in] ee The ecore evas to query + * @param[in] buffer Which selection buffer to ask + * + * @return EINA_TRUE if there is an available selection for the specified buffer. + * + * EINA_TRUE is also returned when the selection is in the window associated with @p ee + * + * @note Due to the asynchronous nature of selection buffers, this method might not return + * the right result when invoked from the selection callback set with ecore_evas_callback_selection_changed_set. + */ +EAPI Eina_Bool ecore_evas_selection_exists(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer); + +/** + * @brief Retrieves the content of the specified selection buffer. + * + * @param[in] ee The ecore evas to query. + * @param[in] buffer Selection buffer to retrieve. + * @param[in] acceptable_types MIME types which are acceptable for the returned Eina_Content. + * The iterator contains plain strings (char *). Ownership is transferred for the iterator but not for the strings. + * This is convenient for the usual case of a hard-coded array of strings, since the iterator can be generated + * on the fly, used and forgotten. + * + * @return An Eina_Future containing an Eina_Content which has one of the types in @p acceptable_type. + * An error is delivered when no matching type is found or when the requested selection buffer is empty. + * + * This method is time consuming, therefore, it is recommended to verify the existence of a selection + * using ecore_evas_selection_exists before calling it. + */ +EAPI Eina_Future* ecore_evas_selection_get(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, Eina_Iterator *acceptable_types); + +/** + * @brief This method is called when the mouse pointer enters or exits the specified window while + * performing a drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] p Position (in window coordinates) where the event occurred. + * @param[in] inside @c EINA_TRUE if the pointer just entered this window. @c EINA_FALSE if it has just exited. + * + * Set this callback using ecore_evas_callback_drop_state_changed_set. + */ +typedef void (*Ecore_Evas_Drag_Finished)(Ecore_Evas *ee, unsigned int seat, void *data, Eina_Bool accepted); + +/** + * @brief Starts a new drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] content The content to delivery at the drop site (ownership is transferred). + * The Eina_Content has data and its associated MIME type, plus a list of alternate types that can be provided. + * @param[in] drag_rep An Ecore_Evas used as a visual representation of the content being dragged. + * It must have the same type as @p ee. This is the transparent object dragged along the mouse pointer to indicate that + * a drag operation is in progress. + * @p terminate_cb will be called when @p drag_rep is not needed anymore and it must be disposed of. + * Use @p data to convey @p drag_rep to @p terminate_cb. For example, if @p drag_rep is owned by an Efl_Window, @p data + * can point to that window. + * @param[in] action Action the target application should perform upon receiving this content. It is entirely up to the + * target application to honor (or even understand) this request. + * @return @c EINA_TRUE if the drag operation has been successfully started. + * + * This method must be called when a drag operation is initiated in order to provide the necessary information. + */ +EAPI Eina_Bool ecore_evas_drag_start(Ecore_Evas *ee, unsigned int seat, Eina_Content *content, Ecore_Evas *drag_rep, const char* action, Ecore_Evas_Drag_Finished terminate_cb, void *data); + +/** + * @brief Cancels an ongoing drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @return @c EINA_TRUE if the drag operation has been successfully cancelled. + * + * The initiator of a drag operation can call this method to abort it. + */ +EAPI Eina_Bool ecore_evas_drag_cancel(Ecore_Evas *ee, unsigned int seat); + +/** + * @brief This method is called when the mouse pointer enters or exits the specified window while + * performing a drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] p Position (in window coordinates) where the event occurred. + * @param[in] inside @c EINA_TRUE if the pointer just entered this window. @c EINA_FALSE if it has just exited. + * + * Set this callback using ecore_evas_callback_drop_state_changed_set. + */ +typedef void (*Ecore_Evas_State_Changed)(Ecore_Evas *ee, unsigned int seat, Eina_Position2D p, Eina_Bool inside); + +/** + * @brief Sets the method (callback) to call when the mouse pointer enters or exits the specified window while + * performing a drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] cb Method to call when the events are received. + * + * Only one such callback can exist for each Ecore_Evas. Calling this method multiple + * times overwrites previous functions. Use a NULL @cb func to stop being notified. + */ +EAPI void ecore_evas_callback_drop_state_changed_set(Ecore_Evas *ee, Ecore_Evas_State_Changed cb); + +/** + * @brief This method is called when the mouse pointer moves over the specified window while + * performing a drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] p Position (in window coordinates) where the event occurred. + * + * Set this callback using ecore_evas_callback_drop_motion_set. + */ + +typedef void (*Ecore_Evas_Motion_Cb)(Ecore_Evas *ee, unsigned int seat, Eina_Position2D p); +/** + * @brief Sets the method (callback) to call when the mouse pointer moves over the specified window while + * performing a drag operation. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] cb Method to call when the events are received. + * + * Only one such callback can exist for each Ecore_Evas. Calling this method multiple + * times overwrites previous functions. Use a NULL @cb func to stop being notified. + */ +EAPI void ecore_evas_callback_drop_motion_set(Ecore_Evas *ee, Ecore_Evas_Motion_Cb cb); + +/** + * @brief This method is called when the mouse pointer is released over the specified window while + * performing a drag operation (thus dropping the dragged content over the window). + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] p Position (in window coordinates) where the event occurred. + * + * The dropped data can be retrieved using ecore_evas_selection_get and the + * ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER buffer. + * + * Set this callback using ecore_evas_callback_drop_drop_set. + */ +typedef void (*Ecore_Evas_Drop_Cb)(Ecore_Evas *ee, unsigned int seat, Eina_Position2D p, const char *action); + +/** + * @brief Sets the method (callback) to call when the mouse pointer is released over the specified window while + * performing a drag operation (thus dropping the dragged content over the window). + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @param[in] cb Method to call when the events are received. + * + * Only one such callback can exist for each Ecore_Evas. Calling this method multiple + * times overwrites previous functions. Use a NULL @cb func to stop being notified. + */ +EAPI void ecore_evas_callback_drop_drop_set(Ecore_Evas *ee, Ecore_Evas_Drop_Cb cb); + +// app calls this (from one of the motion cb's, for example) to know the type (and auto conversion) of the thing being dragged. +// This is the same as calling selection_get and retrieving the types from there (but faster). +/** + * @brief Retrieves the list of types the data currently being dragged can be automatically converted to. + * + * @param[in] ee The Ecore Evas the drag operation started on. + * @return + * + * This can be used in any of the drag and drop callbacks (Ecore_Evas_State_Changed, Ecore_Evas_Motion_Cb and + * Ecore_Evas_Drop_Cb) to check if the data being dragged is acceptable and give the user some early feedback + * before the data is actually dropped on the window. + * + * This is functionally equivalent to calling ecore_evas_selection_get and examining the available types in the + * returned Eina_Content, but much faster since the actual data does not have to be asynchronously requested to the + * initiator application. + */ +EAPI Eina_Accessor* ecore_evas_drop_available_types_get(Ecore_Evas *ee, unsigned int seat); + + /** * @} */ @@ -3685,3 +3898,4 @@ EAPI int ecore_evas_pixmap_depth_get(const Ecore_Evas *ee); #define EAPI #endif + diff --git a/src/lib/ecore_evas/ecore_evas.c b/src/lib/ecore_evas/ecore_evas.c index d3c26ddb19..a95f7903d8 100644 --- a/src/lib/ecore_evas/ecore_evas.c +++ b/src/lib/ecore_evas/ecore_evas.c @@ -652,6 +652,10 @@ ecore_evas_init(void) iface.del = _ecore_evas_animator_del; ecore_evas_object_animator_init(&iface); + ecore_evas_no_matching_type = eina_error_msg_register("No fitting type could be found"); + ecore_evas_no_selection = eina_error_msg_register("No selection available"); + ecore_evas_request_replaced = eina_error_msg_register("Selection request replaced"); + return _ecore_evas_init_count; shutdown_ecore: @@ -2818,7 +2822,7 @@ ecore_evas_shadow_geometry_get(const Ecore_Evas *ee, int *l, int *r, int *t, int if (b) *b = ee->shadow.b; } -EAPI void +EAPI void ecore_evas_pointer_xy_get(const Ecore_Evas *ee, Evas_Coord *x, Evas_Coord *y) { if (x) *x = 0; @@ -2828,7 +2832,7 @@ ecore_evas_pointer_xy_get(const Ecore_Evas *ee, Evas_Coord *x, Evas_Coord *y) IFE; } -EAPI Eina_Bool +EAPI Eina_Bool ecore_evas_pointer_warp(const Ecore_Evas *ee, Evas_Coord x, Evas_Coord y) { ECORE_EVAS_CHECK(ee, EINA_FALSE); @@ -2905,7 +2909,7 @@ ecore_evas_pixmap_visual_get(const Ecore_Evas *ee) return NULL; } -EAPI unsigned long +EAPI unsigned long ecore_evas_pixmap_colormap_get(const Ecore_Evas *ee) { ECORE_EVAS_CHECK(ee, 0); @@ -2932,7 +2936,7 @@ ecore_evas_pixmap_colormap_get(const Ecore_Evas *ee) return 0; } -EAPI int +EAPI int ecore_evas_pixmap_depth_get(const Ecore_Evas *ee) { ECORE_EVAS_CHECK(ee, 0); @@ -3524,6 +3528,9 @@ _ecore_evas_free(Ecore_Evas *ee) free(iface); ee->engine.ifaces = NULL; + + if (ee->fallback_interface) + fallback_selection_shutdown(ee); free(ee); } @@ -3542,7 +3549,7 @@ _ecore_evas_idle_timeout_update(Ecore_Evas *ee) { if (ee->engine.idle_flush_timer) ecore_timer_del(ee->engine.idle_flush_timer); - ee->engine.idle_flush_timer = + ee->engine.idle_flush_timer = ecore_timer_loop_add(IDLE_FLUSH_TIME, _ecore_evas_cb_idle_flush, ee); } @@ -4007,7 +4014,7 @@ ecore_evas_software_x11_pixmap_new(const char *disp_name, Ecore_X_Window parent, } -EAPI Ecore_X_Pixmap +EAPI Ecore_X_Pixmap ecore_evas_software_x11_pixmap_get(const Ecore_Evas *ee) { Ecore_Evas_Interface_Software_X11 *iface; @@ -4082,7 +4089,7 @@ ecore_evas_gl_x11_pixmap_new(const char *disp_name, Ecore_X_Window parent, int x } -EAPI Ecore_X_Pixmap +EAPI Ecore_X_Pixmap ecore_evas_gl_x11_pixmap_get(const Ecore_Evas *ee) { Ecore_Evas_Interface_Gl_X11 *iface; @@ -5449,3 +5456,344 @@ _ecore_evas_animator_thaw(Ecore_Animator *in) EINA_INLIST_GET(animator)); _ticking_start(ee); } + +EAPI void +ecore_evas_callback_selection_changed_set(Ecore_Evas *ee, Ecore_Evas_Selection_Changed_Cb func) +{ + ECORE_EVAS_CHECK(ee); + ee->func.fn_selection_changed = func; +} + +static Ecore_Evas_Selection_Seat_Buffers* +_fetch_selection_buffers_of_seat(Ecore_Evas *ee, unsigned int seat, Eina_Bool create) +{ + Ecore_Evas_Selection_Seat_Buffers *buffers; + if (!ee->selection_buffers) + ee->selection_buffers = eina_hash_int32_new(free); + + buffers = eina_hash_find(ee->selection_buffers, &seat); + + if (!buffers && create) + { + buffers = calloc(1, sizeof(Ecore_Evas_Selection_Seat_Buffers)); + buffers->seat = seat; + eina_hash_add(ee->selection_buffers, &seat, buffers); + } + return buffers; +} + +static Eina_Bool +_deliver_cb(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, const char *type, Eina_Rw_Slice *slice) +{ + Ecore_Evas_Selection_Seat_Buffers *buffers; + Eina_Content *content; + Eina_Content *converted = NULL; + Eina_Bool result = EINA_FALSE; + + INF("Delivery request on seat %d in buffer %d", seat, buffer); + + buffers = _fetch_selection_buffers_of_seat(ee, seat, EINA_FALSE); + EINA_SAFETY_ON_NULL_GOTO(buffers, free_everything); + content = buffers->selection_buffer[buffer]; + EINA_SAFETY_ON_NULL_GOTO(content, free_everything); + if (!eina_streq(type, eina_content_type_get(content))) + converted = eina_content_convert(content, type); + else + converted = content; + + EINA_SAFETY_ON_NULL_GOTO(converted, free_everything); + *slice = eina_slice_dup(eina_content_data_get(converted)); + result = EINA_TRUE; + + if (buffer == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) + { + ee->drag.accepted = EINA_TRUE; + } + +free_everything: + if (converted && content && !eina_streq(type, eina_content_type_get(content))) + eina_content_free(converted); + + return result; +} + +static void +_cancel_cb(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer) +{ + Ecore_Evas_Selection_Seat_Buffers *buffers; + + INF("Cancel request on seat %d in buffer %d", seat, buffer); + + buffers = _fetch_selection_buffers_of_seat(ee, seat, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN(buffers); + EINA_SAFETY_ON_FALSE_RETURN(buffers->selection_buffer[buffer]); + eina_content_free(buffers->selection_buffer[buffer]); + buffers->selection_buffer[buffer] = NULL; + + if (buffer == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) + { + ee->drag.rep = NULL; + if (ee->drag.free) + ee->drag.free(ee, seat, ee->drag.data, EINA_FALSE); + ee->drag.free = NULL; + } +} + +#define CALL(call) (ee->engine.func->fn_ ##call ? : fallback_ ##call) + +static Eina_Array* +_iterator_to_array(Eina_Iterator *iter, const char *existing_type) +{ + Eina_Array *ret = eina_array_new(10); + const char *type; + + if (existing_type) + eina_array_push(ret, existing_type); + + EINA_ITERATOR_FOREACH(iter, type) + { + eina_array_push(ret, type); + } + eina_iterator_free(iter); + + return ret; +} + +EAPI Eina_Bool +ecore_evas_selection_set(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, Eina_Content *content) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(buffer >= 0 && buffer < ECORE_EVAS_SELECTION_BUFFER_LAST, EINA_FALSE); + Eina_Iterator *available_type = NULL; + Eina_Bool success; + Ecore_Evas_Selection_Seat_Buffers *buffers; + + INF("Selection set on seat %d in buffer %d", seat, buffer); + + buffers = _fetch_selection_buffers_of_seat(ee, seat, EINA_TRUE); + + if (content) + available_type = eina_content_possible_conversions(content); + + if (buffer == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) + { + ERR("You cannot set a selection with this API, please use the API to start a drag operation"); + return EINA_FALSE; + } + + success = CALL(selection_claim)(ee, seat, buffer, _iterator_to_array(available_type, content ? eina_content_type_get(content) : NULL), content ? _deliver_cb : NULL, content ? _cancel_cb : NULL); + if (success) + { + EINA_SAFETY_ON_FALSE_RETURN_VAL(buffers->selection_buffer[buffer] == NULL, EINA_FALSE); + //keep this after the claim, the claim might call cancel, which would overwrite this. + buffers->selection_buffer[buffer] = content; + } + else + { + eina_content_free(content); + } + + return success; +} + +EAPI Eina_Bool +ecore_evas_selection_exists(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + EINA_SAFETY_ON_FALSE_RETURN_VAL(buffer >= 0 && buffer < ECORE_EVAS_SELECTION_BUFFER_LAST, EINA_FALSE); + Ecore_Evas_Selection_Seat_Buffers *buffers; + + INF("Exists request on seat %d in buffer %d", seat, buffer); + + buffers = _fetch_selection_buffers_of_seat(ee, seat, EINA_TRUE); + if (buffers->selection_buffer[buffer]) + return EINA_TRUE; + else + { + return CALL(selection_has_owner)(ee, seat, buffer); + } +} + +static Eina_Array* +_iterator_to_array_stringshared(Eina_Iterator *iter) +{ + Eina_Array *ret = eina_array_new(10); + const char *type; + + EINA_ITERATOR_FOREACH(iter, type) + { + eina_array_push(ret, eina_stringshare_add(type)); + } + eina_iterator_free(iter); + + return ret; +} + +EAPI Eina_Future* +ecore_evas_selection_get(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, Eina_Iterator *acceptable_types) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, NULL); + EINA_SAFETY_ON_FALSE_RETURN_VAL(buffer >= 0 && buffer < ECORE_EVAS_SELECTION_BUFFER_LAST, NULL); + + INF("Selection get request on seat %d in buffer %d", seat, buffer); + + return CALL(selection_request)(ee, seat, buffer, _iterator_to_array_stringshared(acceptable_types)); +} + +EAPI Eina_Bool +ecore_evas_drag_start(Ecore_Evas *ee, unsigned int seat, Eina_Content *content, Ecore_Evas *drag_rep, const char* action, Ecore_Evas_Drag_Finished terminate_cb, void *data) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + EINA_SAFETY_ON_NULL_RETURN_VAL(content, EINA_FALSE); + Eina_Iterator *available_type = eina_content_possible_conversions(content); + Eina_Bool success; + Ecore_Evas_Selection_Seat_Buffers *buffers; + + INF("Drag start on seat %d", seat); + + buffers = _fetch_selection_buffers_of_seat(ee, seat, EINA_TRUE); + success = CALL(dnd_start)(ee, seat, _iterator_to_array(available_type, eina_content_type_get(content)), drag_rep, _deliver_cb, _cancel_cb, action); + EINA_SAFETY_ON_FALSE_RETURN_VAL(buffers->selection_buffer[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER] == NULL, EINA_FALSE); + //keep this after the claim, the claim might call cancel, which would overwrite this. + buffers->selection_buffer[ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER] = content; + + ee->drag.rep = drag_rep; + ee->drag.free = terminate_cb; + ee->drag.data = data; + ee->drag.accepted = EINA_FALSE; + + return success; +} + +EAPI Eina_Bool +ecore_evas_drag_cancel(Ecore_Evas *ee, unsigned int seat) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(ee, EINA_FALSE); + + INF("Drag cancel on seat %d", seat); + + return CALL(dnd_stop)(ee, seat); +} + +EAPI void +ecore_evas_callback_drop_motion_set(Ecore_Evas *ee, Ecore_Evas_Motion_Cb cb) +{ + ECORE_EVAS_CHECK(ee); + ee->func.fn_dnd_motion = cb; +} + +EAPI void +ecore_evas_callback_drop_state_changed_set(Ecore_Evas *ee, Ecore_Evas_State_Changed cb) +{ + ECORE_EVAS_CHECK(ee); + ee->func.fn_dnd_state_change = cb; +} + +EAPI void +ecore_evas_callback_drop_drop_set(Ecore_Evas *ee, Ecore_Evas_Drop_Cb cb) +{ + ECORE_EVAS_CHECK(ee); + ee->func.fn_dnd_drop = cb; +} + +typedef struct { + Eina_Array *available_mime_types; + Eina_Position2D pos; +} Ecore_Evas_Active_Dnd; + +static void +_ecore_evas_active_dnd_free(Ecore_Evas_Active_Dnd *dnd) +{ + eina_array_free(dnd->available_mime_types); + free(dnd); +} + +EAPI void +ecore_evas_dnd_enter(Ecore_Evas *ee, unsigned int seat, Eina_Iterator *available_types, Eina_Position2D pos) +{ + Eina_Stringshare *s; + Ecore_Evas_Active_Dnd *dnd; + + ECORE_EVAS_CHECK(ee); + if (!ee->active_drags) + { + ee->active_drags = eina_hash_int32_new((Eina_Free_Cb)_ecore_evas_active_dnd_free); + } + + dnd = calloc(1, sizeof(Ecore_Evas_Active_Dnd)); + dnd->available_mime_types = eina_array_new(5); + eina_hash_add(ee->active_drags, &seat, dnd); + + EINA_ITERATOR_FOREACH(available_types, s) + { + eina_array_push(dnd->available_mime_types, s); + } + eina_iterator_free(available_types); + + if (ee->func.fn_dnd_state_change) + ee->func.fn_dnd_state_change(ee, seat, pos, EINA_TRUE); +} + +EAPI void +ecore_evas_dnd_position_set(Ecore_Evas *ee, unsigned int seat, Eina_Position2D pos) +{ + Ecore_Evas_Active_Dnd *dnd; + + ECORE_EVAS_CHECK(ee); + EINA_SAFETY_ON_NULL_RETURN(ee->active_drags); + dnd = eina_hash_find(ee->active_drags, &seat); + EINA_SAFETY_ON_NULL_RETURN(dnd); + dnd->pos = pos; + if (ee->func.fn_dnd_motion) + ee->func.fn_dnd_motion(ee, seat, pos); +} + +EAPI void +ecore_evas_dnd_leave(Ecore_Evas *ee, unsigned int seat, Eina_Position2D pos) +{ + Ecore_Evas_Active_Dnd *dnd; + + ECORE_EVAS_CHECK(ee); + EINA_SAFETY_ON_NULL_RETURN(ee->active_drags); + dnd = eina_hash_find(ee->active_drags, &seat); + EINA_SAFETY_ON_NULL_RETURN(dnd); + + if (ee->func.fn_dnd_state_change) + ee->func.fn_dnd_state_change(ee, seat, pos, EINA_FALSE); + eina_hash_del(ee->active_drags, &seat, dnd); + if (eina_hash_population(ee->active_drags) == 0) + { + eina_hash_free(ee->active_drags); + ee->active_drags = NULL; + } +} + +EAPI Eina_Position2D +ecore_evas_dnd_pos_get(Ecore_Evas *ee, unsigned int seat) +{ + Ecore_Evas_Active_Dnd *dnd; + + ECORE_EVAS_CHECK_GOTO(ee, err); + EINA_SAFETY_ON_NULL_RETURN_VAL(ee->active_drags, EINA_POSITION2D(0, 0)); + dnd = eina_hash_find(ee->active_drags, &seat); + EINA_SAFETY_ON_NULL_RETURN_VAL(dnd, EINA_POSITION2D(0, 0)); + + return dnd->pos; +err: + return EINA_POSITION2D(0, 0); +} + +EAPI Eina_Accessor* +ecore_evas_drop_available_types_get(Ecore_Evas *ee, unsigned int seat) +{ + Ecore_Evas_Active_Dnd *dnd; + + ECORE_EVAS_CHECK_GOTO(ee, err); + EINA_SAFETY_ON_NULL_RETURN_VAL(ee->active_drags, NULL); + dnd = eina_hash_find(ee->active_drags, &seat); + EINA_SAFETY_ON_NULL_RETURN_VAL(dnd, NULL); + + return eina_array_accessor_new(dnd->available_mime_types); +err: + return NULL; +} diff --git a/src/lib/ecore_evas/ecore_evas_fallback_selection.c b/src/lib/ecore_evas/ecore_evas_fallback_selection.c new file mode 100644 index 0000000000..1088e617f7 --- /dev/null +++ b/src/lib/ecore_evas/ecore_evas_fallback_selection.c @@ -0,0 +1,115 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include "ecore_private.h" +#include "Ecore_Evas.h" +#include "ecore_evas_private.h" +#include + +typedef struct { + Ecore_Evas_Selection_Callbacks callbacks[ECORE_EVAS_SELECTION_BUFFER_LAST]; + int seat; +} Ecore_Evas_Fallback_Selection_Data; + +static Ecore_Evas_Fallback_Selection_Data data[ECORE_EVAS_SELECTION_BUFFER_LAST]; + +void +fallback_selection_shutdown(Ecore_Evas *ee) +{ + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + if (data->callbacks[i].cancel) + data->callbacks[i].cancel(ee, data->seat, i); + } +} + +Eina_Bool +fallback_selection_claim(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel) +{ + Ecore_Evas_Selection_Callbacks *callbacks = &data->callbacks[selection]; + + if (callbacks->cancel) + { + callbacks->cancel(ee, data->seat, selection); + eina_array_free(callbacks->available_types); + } + + callbacks->delivery = delivery; + callbacks->cancel = cancel; + callbacks->available_types = available_types; + data->seat = seat; + + if (ee->func.fn_selection_changed) + ee->func.fn_selection_changed(ee, seat, selection); + + return EINA_TRUE; +} + +Eina_Bool +fallback_selection_has_owner(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection EINA_UNUSED) +{ + return EINA_FALSE; //if the real selection buffer does not contain it, then we dont know it either. +} + +Eina_Stringshare* +available_types(Eina_Array *acceptable_types, Eina_Array *available_types) +{ + unsigned int found_type_id = INT_MAX; + Eina_Stringshare *found_type = NULL; + Eina_Stringshare *type; + + for (unsigned int i = 0; i < eina_array_count_get(acceptable_types); ++i) + { + unsigned int out = -1; + + type = eina_array_data_get(acceptable_types, i); + + if (!eina_array_find(available_types, type, &out)) + continue; + if (out >= found_type_id) + continue; + found_type_id = out; + found_type = type; + eina_stringshare_del(type); + } + eina_array_free(acceptable_types); + + return found_type; +} + +Eina_Future* +fallback_selection_request(Ecore_Evas *ee EINA_UNUSED, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *acceptable_type) +{ + Ecore_Evas_Selection_Callbacks callbacks = data->callbacks[selection]; + Eina_Content *result; + Eina_Stringshare *serving_type; + Eina_Rw_Slice slice_data; + Eina_Value value; + + if (!callbacks.delivery) + return eina_future_resolved(efl_loop_future_scheduler_get(efl_main_loop_get()), eina_value_int_init(0)); + + serving_type = available_types(acceptable_type, callbacks.available_types); + if (!serving_type) + return NULL; //Silent return cause we cannot deliver a good type + + EINA_SAFETY_ON_FALSE_RETURN_VAL(callbacks.delivery(ee, seat, selection, serving_type, &slice_data), NULL); + result = eina_content_new(eina_rw_slice_slice_get(slice_data), serving_type); + value = eina_value_content_init(result); + eina_content_free(result); + + return eina_future_resolved(efl_loop_future_scheduler_get(efl_main_loop_get()), value); +} +Eina_Bool +fallback_dnd_start(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED, Eina_Array *available_types EINA_UNUSED, Ecore_Evas *drag_rep EINA_UNUSED, Ecore_Evas_Internal_Delivery delivery EINA_UNUSED, Ecore_Evas_Internal_Cancel cancel EINA_UNUSED, const char* action EINA_UNUSED) +{ + return EINA_FALSE; +} + +Eina_Bool +fallback_dnd_stop(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED) +{ + return EINA_FALSE; +} diff --git a/src/lib/ecore_evas/ecore_evas_private.h b/src/lib/ecore_evas/ecore_evas_private.h index 474b7a35ed..10191b0152 100644 --- a/src/lib/ecore_evas/ecore_evas_private.h +++ b/src/lib/ecore_evas/ecore_evas_private.h @@ -33,6 +33,10 @@ EAPI extern int _ecore_evas_log_dom; +EAPI Eina_Error ecore_evas_no_matching_type; +EAPI Eina_Error ecore_evas_no_selection; +EAPI Eina_Error ecore_evas_request_replaced; + #ifdef ECORE_EVAS_DEFAULT_LOG_COLOR # undef ECORE_EVAS_DEFAULT_LOG_COLOR #endif @@ -78,6 +82,13 @@ typedef struct _Ecore_Evas_Interface Ecore_Evas_Interface; typedef struct _Ecore_Evas_Aux_Hint Ecore_Evas_Aux_Hint; typedef struct _Ecore_Evas_Cursor Ecore_Evas_Cursor; +typedef Eina_Bool (*Ecore_Evas_Internal_Delivery)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer, const char *type, Eina_Rw_Slice *slice); +typedef void (*Ecore_Evas_Internal_Cancel)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer buffer); +typedef struct { + Ecore_Evas_Internal_Delivery delivery; + Ecore_Evas_Internal_Cancel cancel; + Eina_Array *available_types; +} Ecore_Evas_Selection_Callbacks; /* Engines interfaces */ struct _Ecore_Evas_Engine_Func { @@ -171,6 +182,12 @@ struct _Ecore_Evas_Engine_Func Eina_Bool (*fn_prepare)(Ecore_Evas *ee); double (*fn_last_tick_get)(Ecore_Evas *ee); + + Eina_Bool (*fn_selection_claim)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel); + Eina_Bool (*fn_selection_has_owner)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection); + Eina_Future* (*fn_selection_request)(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *acceptable_types); // a future containing a Eina_Content, type must be in acceptable_types + Eina_Bool (*fn_dnd_start)(Ecore_Evas *ee, unsigned int seat, Eina_Array *available_types, Ecore_Evas *drag_rep, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel, const char *action); + Eina_Bool (*fn_dnd_stop)(Ecore_Evas *ee, unsigned int seat); }; struct _Ecore_Evas_Interface @@ -202,6 +219,11 @@ struct _Ecore_Evas_Cursor { int pos_y; }; +typedef struct { + unsigned int seat; + Eina_Content *selection_buffer[ECORE_EVAS_SELECTION_BUFFER_LAST]; +} Ecore_Evas_Selection_Seat_Buffers; + struct _Ecore_Evas { EINA_INLIST; @@ -224,6 +246,8 @@ struct _Ecore_Evas Eina_List *vnc_server; /* @since 1.19 */ + Eina_Hash *selection_buffers; + struct { int x, y, w, h; } req; @@ -259,7 +283,7 @@ struct _Ecore_Evas Eina_Bool supported; // indicate that the underlying window system supports window manager rotation protocol Eina_Bool app_set; // indicate that the ee supports window manager rotation protocol Eina_Bool win_resize; // indicate that the ee will be resized by the WM - int angle; // rotation value which is decided by the WM + int angle; // rotation value which is decided by the WM int w, h; // window size to rotate int preferred_rot; // preferred rotation hint int *available_rots; // array of avaialable rotation values @@ -323,6 +347,10 @@ struct _Ecore_Evas void (*fn_focus_device_out) (Ecore_Evas *ee, Efl_Input_Device *seat); void (*fn_device_mouse_in) (Ecore_Evas *ee, Efl_Input_Device *mouse); void (*fn_device_mouse_out) (Ecore_Evas *ee, Efl_Input_Device *mouse); + void (*fn_selection_changed) (Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection); + void (*fn_dnd_motion) (Ecore_Evas *ee, unsigned int seat, Eina_Position2D p); + void (*fn_dnd_state_change) (Ecore_Evas *ee, unsigned int seat, Eina_Position2D p, Eina_Bool inside); + void (*fn_dnd_drop)(Ecore_Evas *ee, unsigned int seat, Eina_Position2D p, const char *action); } func; Ecore_Evas_Engine engine; @@ -353,6 +381,14 @@ struct _Ecore_Evas unsigned char rotation_changed : 1; } delayed; + Eina_Hash *active_drags; + struct { + Ecore_Evas *rep; + void *data; + Ecore_Evas_Drag_Finished free; + Eina_Bool accepted; + } drag; + int refcount; //#define ECORE_EVAS_ASYNC_RENDER_DEBUG 1 /* TODO: remove me */ #ifdef ECORE_EVAS_ASYNC_RENDER_DEBUG @@ -374,6 +410,7 @@ struct _Ecore_Evas unsigned char first_frame : 1; unsigned char self_del : 1; unsigned char evas_dying : 1; + unsigned char fallback_interface : 1; }; struct _Ecore_Evas_Aux_Hint @@ -486,6 +523,20 @@ EAPI Eina_Bool ecore_evas_render(Ecore_Evas *ee); EAPI Evas *ecore_evas_evas_new(Ecore_Evas *ee, int w, int h); EAPI void ecore_evas_done(Ecore_Evas *ee, Eina_Bool single_window); +EAPI void ecore_evas_dnd_position_set(Ecore_Evas *ee, unsigned int seat, Eina_Position2D pos); +EAPI void ecore_evas_dnd_leave(Ecore_Evas *ee, unsigned int seat, Eina_Position2D pos); +EAPI void ecore_evas_dnd_enter(Ecore_Evas *ee, unsigned int seat, Eina_Iterator *available_types, Eina_Position2D pos); +EAPI Eina_Position2D ecore_evas_dnd_pos_get(Ecore_Evas *ee, unsigned int seat); + + +void fallback_selection_init(Ecore_Evas *ee); +void fallback_selection_shutdown(Ecore_Evas *ee); +Eina_Bool fallback_selection_claim(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *available_types, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel); +Eina_Bool fallback_selection_has_owner(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection); +Eina_Future* fallback_selection_request(Ecore_Evas *ee, unsigned int seat, Ecore_Evas_Selection_Buffer selection, Eina_Array *acceptable_type); +Eina_Bool fallback_dnd_start(Ecore_Evas *ee, unsigned int seat, Eina_Array *available_types, Ecore_Evas *drag_rep, Ecore_Evas_Internal_Delivery delivery, Ecore_Evas_Internal_Cancel cancel, const char* action); +Eina_Bool fallback_dnd_stop(Ecore_Evas *ee, unsigned int seat); + #ifdef IPA_YLNO_ESU_LANRETNI_MLE EAPI Ecore_Evas *_wayland_shm_new(const char *disp_name, Ecore_Window parent, int x, int y, int w, int h, Eina_Bool frame); EAPI Ecore_Evas *_wayland_egl_new(const char *disp_name, Ecore_Window parent, int x, int y, int w, int h, Eina_Bool frame, const int *opt); diff --git a/src/lib/ecore_evas/meson.build b/src/lib/ecore_evas/meson.build index c0fb459a56..890e3c42b3 100644 --- a/src/lib/ecore_evas/meson.build +++ b/src/lib/ecore_evas/meson.build @@ -23,7 +23,8 @@ ecore_evas_src = [ 'ecore_evas_cocoa.h', 'ecore_evas_win32.h', 'ecore_evas_x11.h', - 'ecore_evas_util.c' + 'ecore_evas_util.c', + 'ecore_evas_fallback_selection.c' ] diff --git a/src/lib/elementary/meson.build b/src/lib/elementary/meson.build index 145d958e21..0cb3fc7db9 100644 --- a/src/lib/elementary/meson.build +++ b/src/lib/elementary/meson.build @@ -947,7 +947,7 @@ elementary_src = [ 'efl_ui_collection_view.c', 'efl_ui_pager.c', 'efl_ui_stack.c', - 'efl_ui_separator.c' + 'efl_ui_separator.c', ] elementary_deps = [emile, eo, efl, edje, ethumb, ethumb_client, emotion, ecore_imf, ecore_con, eldbus, efreet, efreet_mime, efreet_trash, eio, atspi, dl, intl] diff --git a/src/modules/ecore_evas/engines/sdl/ecore_evas_sdl.c b/src/modules/ecore_evas/engines/sdl/ecore_evas_sdl.c index 8abc7401b6..ef4ba057b9 100644 --- a/src/modules/ecore_evas/engines/sdl/ecore_evas_sdl.c +++ b/src/modules/ecore_evas/engines/sdl/ecore_evas_sdl.c @@ -455,6 +455,9 @@ static Ecore_Evas_Engine_Func _ecore_sdl_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 }; static Ecore_Evas* diff --git a/src/modules/ecore_evas/engines/wayland/ecore_evas_wayland_common.c b/src/modules/ecore_evas/engines/wayland/ecore_evas_wayland_common.c index c14bdbcd33..e0e2094e2f 100644 --- a/src/modules/ecore_evas/engines/wayland/ecore_evas_wayland_common.c +++ b/src/modules/ecore_evas/engines/wayland/ecore_evas_wayland_common.c @@ -881,14 +881,14 @@ _rotation_do(Ecore_Evas *ee, int rotation, int resize) { /* resize the canvas */ evas_output_size_set(ee->evas, ee->req.w, ee->req.h); - evas_output_viewport_set(ee->evas, 0, 0, + evas_output_viewport_set(ee->evas, 0, 0, ee->req.w, ee->req.h); } else { /* resize the canvas */ evas_output_size_set(ee->evas, ee->req.h, ee->req.w); - evas_output_viewport_set(ee->evas, 0, 0, + evas_output_viewport_set(ee->evas, 0, 0, ee->req.h, ee->req.w); } } @@ -2474,6 +2474,9 @@ static Ecore_Evas_Engine_Func _ecore_wl_engine_func = _ecore_evas_wl_common_pointer_device_xy_get, _ecore_evas_wl_common_prepare, NULL, //fn_last_tick_get + NULL, //fn_selection_claim + NULL, //fn_selection_has_owner + NULL, //fn_selection_request }; static void diff --git a/src/modules/ecore_evas/engines/win32/ecore_evas_win32.c b/src/modules/ecore_evas/engines/win32/ecore_evas_win32.c index 39def9d49f..6198ed4bac 100644 --- a/src/modules/ecore_evas/engines/win32/ecore_evas_win32.c +++ b/src/modules/ecore_evas/engines/win32/ecore_evas_win32.c @@ -1284,6 +1284,9 @@ static Ecore_Evas_Engine_Func _ecore_win32_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 }; #endif /* BUILD_ECORE_EVAS_WIN32 */ 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 abea314d33..fb6eaa63f1 100644 --- a/src/modules/ecore_evas/engines/x/ecore_evas_x.c +++ b/src/modules/ecore_evas/engines/x/ecore_evas_x.c @@ -361,7 +361,7 @@ _ecore_evas_x_aux_hints_supported_update(Ecore_Evas *ee) for (i = 0; i < num; i++) { hint = eina_stringshare_add(str[i]); - ee->prop.aux_hint.supported_list = + ee->prop.aux_hint.supported_list = eina_list_append(ee->prop.aux_hint.supported_list, hint); } @@ -414,7 +414,7 @@ _ecore_evas_x_gl_window_new(Ecore_Evas *ee, Ecore_X_Window parent, int x, int y, op++; einfo->vsync = opt[op]; } -#ifdef EVAS_ENGINE_GL_X11_SWAP_MODE_EXISTS +#ifdef EVAS_ENGINE_GL_X11_SWAP_MODE_EXISTS else if (opt[op] == ECORE_EVAS_GL_X11_OPT_SWAP_MODE) { op++; @@ -845,7 +845,7 @@ _ecore_evas_x_event_property_change(void *data EINA_UNUSED, int type EINA_UNUSED Eina_Bool focus_skip : 1; } prop; } prev; - + prev.x.modal = edata->state.modal; prev.x.sticky = edata->state.sticky; prev.x.maximized_v = edata->state.maximized_v; @@ -856,7 +856,7 @@ _ecore_evas_x_event_property_change(void *data EINA_UNUSED, int type EINA_UNUSED prev.x.fullscreen = edata->state.fullscreen; prev.x.above = edata->state.above; prev.x.below = edata->state.below; - + prev.prop.modal = ee->prop.modal; prev.prop.maximized = ee->prop.maximized; prev.prop.sticky = ee->prop.sticky; @@ -879,7 +879,7 @@ _ecore_evas_x_event_property_change(void *data EINA_UNUSED, int type EINA_UNUSED ee->prop.sticky = EINA_FALSE; ee->prop.fullscreen = EINA_FALSE; // ee->prop.focus_skip = EINA_FALSE; - + ecore_x_netwm_window_state_get(e->win, &state, &num); if (state) { @@ -1253,7 +1253,7 @@ _ecore_evas_x_event_mouse_in(void *data EINA_UNUSED, int type EINA_UNUSED, void if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON; /* pass on event */ if (e->win != ee->prop.window) return ECORE_CALLBACK_PASS_ON; edata = ee->engine.data; -/* +/* { time_t t; char *ct; @@ -1304,7 +1304,7 @@ _ecore_evas_x_event_mouse_in(void *data EINA_UNUSED, int type EINA_UNUSED, void edata->outdelay = NULL; _fake_out(ee); } - + /* if (e->mode != ECORE_X_EVENT_MODE_NORMAL) return 0; */ if (!_ecore_evas_mouse_in_check(ee, NULL)) { @@ -1388,7 +1388,7 @@ _ecore_evas_x_event_mouse_out(void *data EINA_UNUSED, int type EINA_UNUSED, void ecore_timer_del(edata->outdelay); edata->outdelay = NULL; } - + // if (e->mode != ECORE_X_EVENT_MODE_NORMAL) return 0; // printf("OUT: ee->in=%i, e->mode=%i, e->detail=%i, dount_count=%i\n", // ee->in, e->mode, e->detail, evas_event_down_count_get(ee->evas)); @@ -1417,7 +1417,7 @@ _ecore_evas_x_event_window_focus_in(void *data EINA_UNUSED, int type EINA_UNUSED ee = ecore_event_window_match(e->win); if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON; /* pass on event */ if (e->win != ee->prop.window) return ECORE_CALLBACK_PASS_ON; -//xx// filtering with these doesnt help +//xx// filtering with these doesnt help //xx// if (e->mode == ECORE_X_EVENT_MODE_UNGRAB) return ECORE_CALLBACK_PASS_ON; _ecore_evas_focus_device_set(ee, NULL, EINA_TRUE); return ECORE_CALLBACK_PASS_ON; @@ -1433,7 +1433,7 @@ _ecore_evas_x_event_window_focus_out(void *data EINA_UNUSED, int type EINA_UNUSE ee = ecore_event_window_match(e->win); if ((!ee) || (ee->ignore_events)) return ECORE_CALLBACK_PASS_ON; /* pass on event */ if (e->win != ee->prop.window) return ECORE_CALLBACK_PASS_ON; -//xx// filtering with these doesnt help +//xx// filtering with these doesnt help //xx// if (e->mode == ECORE_X_EVENT_MODE_GRAB) return ECORE_CALLBACK_PASS_ON; // if (ee->prop.fullscreen) @@ -2145,7 +2145,7 @@ _ecore_evas_x_resize(Ecore_Evas *ee, int w, int h) } /* check for valid property window - * + * * NB: If we do not have one, check for valid pixmap rendering */ if (!ee->prop.window) { @@ -2153,7 +2153,7 @@ _ecore_evas_x_resize(Ecore_Evas *ee, int w, int h) if ((edata->pixmap.w != vw) || (edata->pixmap.h != vh)) { /* free the backing pixmap */ - if (edata->pixmap.back) + if (edata->pixmap.back) ecore_x_pixmap_free(edata->pixmap.back); } } @@ -2918,7 +2918,7 @@ _ecore_evas_x_aspect_set(Ecore_Evas *ee, double aspect) ee->prop.aspect = aspect; _ecore_evas_x_size_pos_hints_update(ee); -// netwm state +// netwm state // if (ee->should_be_visible) // ecore_x_netwm_state_request_send(ee->prop.window, ee->engine.x.win_root, // ECORE_X_WINDOW_STATE_STICKY, -1, sticky); @@ -3545,14 +3545,14 @@ norandr: if (!found) goto norandr; } -static void +static void _ecore_evas_x_pointer_xy_get(const Ecore_Evas *ee, Evas_Coord *x, Evas_Coord *y) { if (ee->prop.window) ecore_x_pointer_xy_get(ee->prop.window, x, y); } -static Eina_Bool +static Eina_Bool _ecore_evas_x_pointer_warp(const Ecore_Evas *ee, Evas_Coord x, Evas_Coord y) { return ecore_x_pointer_warp(ee->prop.window, x, y); @@ -3754,6 +3754,9 @@ 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 }; /* @@ -3772,19 +3775,19 @@ _ecore_evas_x_render_pre(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ /* printf("\tPixman Size: %d %d\n", edata->pixmap.w, edata->pixmap.h); */ /* printf("\tEE Size: %d %d\n", ee->w, ee->h); */ - /* before rendering to the back buffer pixmap, we should check the - * size. If the back buffer is not the proper size, destroy it and + /* before rendering to the back buffer pixmap, we should check the + * size. If the back buffer is not the proper size, destroy it and * create a new one at the proper size */ if ((edata->pixmap.w != ee->w) || (edata->pixmap.h != ee->h)) { int fw = 0, fh = 0; /* free the backing pixmap */ - if (edata->pixmap.back) + if (edata->pixmap.back) ecore_x_pixmap_free(edata->pixmap.back); - edata->pixmap.back = - ecore_x_pixmap_new(edata->win_root, ee->w, ee->h, + edata->pixmap.back = + ecore_x_pixmap_new(edata->win_root, ee->w, ee->h, edata->pixmap.depth); evas_output_framespace_get(ee->evas, NULL, NULL, &fw, &fh); @@ -3803,7 +3806,7 @@ _ecore_evas_x_render_pre(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo)) { - ERR("evas_engine_info_set() init engine '%s' failed.", + ERR("evas_engine_info_set() init engine '%s' failed.", ee->driver); } } @@ -3821,7 +3824,7 @@ _ecore_evas_x_render_pre(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo)) { - ERR("evas_engine_info_set() init engine '%s' failed.", + ERR("evas_engine_info_set() init engine '%s' failed.", ee->driver); } } @@ -3844,8 +3847,8 @@ _ecore_evas_x_flush_post(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ /* printf("\tBack Pixmap: %d\n", edata->pixmap.back); */ /* printf("\tFront Pixmap: %d\n", edata->pixmap.front); */ - /* done drawing to the back buffer. flip it to the front so that - * any calls to "fetch pixmap" will return the front buffer already + /* done drawing to the back buffer. flip it to the front so that + * any calls to "fetch pixmap" will return the front buffer already * pre-rendered */ /* record the current front buffer */ @@ -3870,7 +3873,7 @@ _ecore_evas_x_flush_post(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo)) { - ERR("evas_engine_info_set() init engine '%s' failed.", + ERR("evas_engine_info_set() init engine '%s' failed.", ee->driver); } } @@ -3888,7 +3891,7 @@ _ecore_evas_x_flush_post(void *data, Evas *e EINA_UNUSED, void *event_info EINA_ if (!evas_engine_info_set(ee->evas, (Evas_Engine_Info *)einfo)) { - ERR("evas_engine_info_set() init engine '%s' failed.", + ERR("evas_engine_info_set() init engine '%s' failed.", ee->driver); } } @@ -4307,9 +4310,9 @@ ecore_evas_software_x11_pixmap_new_internal(const char *disp_name, Ecore_X_Windo edata->pixmap.colormap = einfo->info.colormap; /* create front and back pixmaps for double-buffer rendering */ - edata->pixmap.front = + edata->pixmap.front = ecore_x_pixmap_new(parent, w, h, edata->pixmap.depth); - edata->pixmap.back = + edata->pixmap.back = ecore_x_pixmap_new(parent, w, h, edata->pixmap.depth); einfo->info.drawable = edata->pixmap.back; @@ -4322,7 +4325,7 @@ ecore_evas_software_x11_pixmap_new_internal(const char *disp_name, Ecore_X_Windo } } - /* FIXME: Allow of these set properties or do something with the + /* FIXME: Allow of these set properties or do something with the * ee->prop.window (x window), which we do not have in pixmap case */ /* _ecore_evas_x_hints_update(ee); */ @@ -4370,7 +4373,7 @@ _ecore_evas_software_x11_pixmap_visual_get(const Ecore_Evas *ee) return edata->pixmap.visual; } -static unsigned long +static unsigned long _ecore_evas_software_x11_pixmap_colormap_get(const Ecore_Evas *ee) { if (!(!strcmp(ee->driver, "software_x11"))) return 0; @@ -4378,7 +4381,7 @@ _ecore_evas_software_x11_pixmap_colormap_get(const Ecore_Evas *ee) return edata->pixmap.colormap; } -static int +static int _ecore_evas_software_x11_pixmap_depth_get(const Ecore_Evas *ee) { if (!(!strcmp(ee->driver, "software_x11"))) return 0; @@ -4719,9 +4722,9 @@ ecore_evas_gl_x11_pixmap_new_internal(const char *disp_name, Ecore_X_Window pare edata->pixmap.colormap = einfo->info.colormap; /* create front and back pixmaps for double-buffer rendering */ - edata->pixmap.front = + edata->pixmap.front = ecore_x_pixmap_new(parent, w, h, edata->pixmap.depth); - edata->pixmap.back = + edata->pixmap.back = ecore_x_pixmap_new(parent, w, h, edata->pixmap.depth); einfo->info.drawable = edata->pixmap.back; @@ -4779,7 +4782,7 @@ _ecore_evas_gl_x11_pixmap_visual_get(const Ecore_Evas *ee) return edata->pixmap.visual; } -static unsigned long +static unsigned long _ecore_evas_gl_x11_pixmap_colormap_get(const Ecore_Evas *ee) { if (!(!strcmp(ee->driver, "opengl_x11"))) return 0; @@ -4787,7 +4790,7 @@ _ecore_evas_gl_x11_pixmap_colormap_get(const Ecore_Evas *ee) return edata->pixmap.colormap; } -static int +static int _ecore_evas_gl_x11_pixmap_depth_get(const Ecore_Evas *ee) { if (!(!strcmp(ee->driver, "opengl_x11"))) return 0; diff --git a/src/tests/ecore/ecore_suite.c b/src/tests/ecore/ecore_suite.c index b9248a814a..a0f6d19aba 100644 --- a/src/tests/ecore/ecore_suite.c +++ b/src/tests/ecore/ecore_suite.c @@ -30,6 +30,7 @@ static const Efl_Test_Case etc[] = { { "Ecore_Job", ecore_test_ecore_job }, { "Ecore_Args", ecore_test_ecore_args }, { "Ecore_Pipe", ecore_test_ecore_pipe }, + { "Ecore_Evas_Selection", ecore_test_ecore_evas_selection }, { NULL, NULL } }; diff --git a/src/tests/ecore/ecore_suite.h b/src/tests/ecore/ecore_suite.h index a3327412fe..2621535f59 100644 --- a/src/tests/ecore/ecore_suite.h +++ b/src/tests/ecore/ecore_suite.h @@ -23,5 +23,6 @@ void ecore_test_ecore_file(TCase *tc); void ecore_test_ecore_job(TCase *tc); void ecore_test_ecore_args(TCase *tc); void ecore_test_ecore_pipe(TCase *tc); +void ecore_test_ecore_evas_selection(TCase *tc); #endif /* _ECORE_SUITE_H */ diff --git a/src/tests/ecore/ecore_test_ecore_evas.c b/src/tests/ecore/ecore_test_ecore_evas.c index 2e53f62082..0e7b09c41b 100644 --- a/src/tests/ecore/ecore_test_ecore_evas.c +++ b/src/tests/ecore/ecore_test_ecore_evas.c @@ -3,6 +3,7 @@ #endif #include +#include #include "ecore_suite.h" @@ -70,8 +71,51 @@ EFL_START_TEST(ecore_test_ecore_evas_cocoa) } EFL_END_TEST +static Eina_Value +_verify_and_exit(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED) +{ + ck_assert_ptr_eq(eina_value_type_get(&value), EINA_VALUE_TYPE_CONTENT); + Eina_Content *content = eina_value_to_content(&value); + Eina_Content *reference = data; + + ck_assert_int_eq(eina_content_data_get(content).len, eina_content_data_get(reference).len); + ck_assert_str_eq(eina_content_data_get(content).mem, eina_content_data_get(reference).mem); + ck_assert_str_eq(eina_content_type_get(content), eina_content_type_get(reference)); + + efl_loop_quit(efl_main_loop_get(), eina_value_int_init(0)); + + return EINA_VALUE_EMPTY; +} + +EFL_START_TEST(ecore_test_ecore_evas_fallback_selection) +{ + Ecore_Evas *ee; + ecore_evas_init(); + + ee = ecore_evas_buffer_new(WINDOW_WIDTH, WINDOW_HEIGHT); + fail_if(ee == NULL); + + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + ck_assert_int_eq(ecore_evas_selection_exists(ee, 0, i), EINA_FALSE); + } + Eina_Content *content = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL("asdf"), "text/plain"); + Eina_Content *ref = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL("asdf"), "text/plain"); + ecore_evas_selection_set(ee, 0, ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER, content); + + const char *types[] = {eina_stringshare_add("text/plain")}; + + Eina_Future *f = ecore_evas_selection_get(ee, 0, ECORE_EVAS_SELECTION_BUFFER_SELECTION_BUFFER, EINA_C_ARRAY_ITERATOR_NEW(types)); + ck_assert_ptr_ne(f, NULL); + eina_future_then(f, _verify_and_exit, ref); + efl_task_run(efl_main_loop_get()); + ecore_evas_shutdown(); +} +EFL_END_TEST + void ecore_test_ecore_evas(TCase *tc) { tcase_add_test(tc, ecore_test_ecore_evas_associate); tcase_add_test(tc, ecore_test_ecore_evas_cocoa); + tcase_add_test(tc, ecore_test_ecore_evas_fallback_selection); } diff --git a/src/tests/ecore/ecore_test_ecore_evas_selection.c b/src/tests/ecore/ecore_test_ecore_evas_selection.c new file mode 100644 index 0000000000..9efc5fe722 --- /dev/null +++ b/src/tests/ecore/ecore_test_ecore_evas_selection.c @@ -0,0 +1,89 @@ +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "ecore_suite.h" + +#define WINDOW_HEIGHT 200 +#define WINDOW_WIDTH 200 + +static int log_abort; +static int log_abort_level; + +void +fail_on_errors_teardown(void) +{ + eina_log_abort_on_critical_set(log_abort); + eina_log_abort_on_critical_level_set(log_abort_level); +} + +void +fail_on_errors_setup(void) +{ + log_abort = eina_log_abort_on_critical_get(); + log_abort_level = eina_log_abort_on_critical_level_get(); + eina_log_abort_on_critical_level_set(2); + eina_log_abort_on_critical_set(1); +} + +static Ecore_Evas *ee; + +void +_setup(void) +{ + ecore_evas_init(); + ee = ecore_evas_buffer_new(50, 50); +} + +void +_teardown(void) +{ + ecore_evas_free(ee); + ecore_evas_shutdown(); +} + +EFL_START_TEST(ecore_test_selection_get_twice) +{ + //this is just running this and checking that we do not error + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + if (i == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) continue; + const char *types[] = {eina_stringshare_add("text/plain")}; + ck_assert_ptr_ne(ecore_evas_selection_get(ee, 0, i, EINA_C_ARRAY_ITERATOR_NEW(types)), NULL); + } + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + if (i == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) continue; + const char *types[] = {eina_stringshare_add("text/plain")}; + ck_assert_ptr_ne(ecore_evas_selection_get(ee, 0, i, EINA_C_ARRAY_ITERATOR_NEW(types)), NULL); + } +} +EFL_END_TEST + +EFL_START_TEST(ecore_test_selection_claim_twice) +{ + //this is just running this and checking that we do not error + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + if (i == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) continue; + Eina_Content *content = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL("asdf"), "text/plain"); + ck_assert_int_eq(ecore_evas_selection_set(ee, 0, i, content), EINA_TRUE); + } + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + if (i == ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER) continue; + Eina_Content *content = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL("asdf"), "text/plain"); + ck_assert_int_eq(ecore_evas_selection_set(ee, 0, i, content), EINA_TRUE); + } +} +EFL_END_TEST +void ecore_test_ecore_evas_selection(TCase *tc) +{ + tcase_add_checked_fixture(tc, fail_on_errors_setup, fail_on_errors_teardown); + tcase_add_checked_fixture(tc, _setup, _teardown); + tcase_add_test(tc, ecore_test_selection_get_twice); + tcase_add_test(tc, ecore_test_selection_claim_twice); +} diff --git a/src/tests/ecore/meson.build b/src/tests/ecore/meson.build index 9ce6848ce2..48c9350638 100644 --- a/src/tests/ecore/meson.build +++ b/src/tests/ecore/meson.build @@ -13,6 +13,7 @@ ecore_suite_src = [ 'ecore_test_job.c', 'ecore_test_args.c', 'ecore_test_pipe.c', + 'ecore_test_ecore_evas_selection.c', 'ecore_suite.h' ] diff --git a/src/tests/elementary/efl_ui_window_cnp_dnd_slave.c b/src/tests/elementary/efl_ui_window_cnp_dnd_slave.c new file mode 100644 index 0000000000..bf8c6d50bd --- /dev/null +++ b/src/tests/elementary/efl_ui_window_cnp_dnd_slave.c @@ -0,0 +1,191 @@ +#define EFL_BETA_API_SUPPORT 1 + +#include +#include +#include +#include "efl_ui_grid_view.eo.h" + +static Ecore_Evas *ee; + +static Eina_Value +_deliverty_cb(void *data, const Eina_Value value, const Eina_Future *dead_future EINA_UNUSED) +{ + Ecore_Evas_Selection_Buffer buffer = (intptr_t)data; + Eina_Content *content; + + if (eina_value_type_get(&value) != EINA_VALUE_TYPE_CONTENT) + { + char *error = eina_value_to_string(&value); + printf("Value not a content, message: \"%s\"\n", error); + return EINA_VALUE_EMPTY; + } + + content = eina_value_to_content(&value); + printf("Got Content of selection %d with type %s\n", buffer, eina_content_type_get(content)); + if (!strncmp(eina_content_type_get(content), "text", strlen("text"))) + { + printf("Content: %s\n", (char*)eina_content_data_get(content).mem); + } + + return EINA_VALUE_EMPTY; +} + +static void +_selection_changed(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection) +{ + printf("Selection %d of %p has changed\n", selection, ee); +} + +static void +_request_selection(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Ecore_Evas_Selection_Buffer selection) +{ + const char *types[] = {eina_stringshare_add("text/plain"), eina_stringshare_add("text/plain;charset=utf-8")}; + printf("Selection %d of %p has changed\n", selection, ee); + Eina_Future *future = ecore_evas_selection_get(ee, 0, selection, EINA_C_ARRAY_ITERATOR_NEW(types)); + eina_future_then(future, _deliverty_cb, .data = ((void*)(intptr_t)selection)); +} + +static void +_motion_cb(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Position2D p) +{ + printf("Drag and Drop has moved on the window %p (%d, %d)\n", ee, p.x, p.y); +} + +static void +_enter_state_change_cb(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Position2D p, Eina_Bool inside) +{ + if (inside) + printf("Drag and Drop has entered the window %p (%d, %d)\n", ee, p.x, p.y); + else + printf("Drag and Drop has left the window %p (%d, %d)\n", ee, p.x, p.y); +} + +static void +_drop_cb(Ecore_Evas *ee, unsigned int seat EINA_UNUSED, Eina_Position2D p, const char *action EINA_UNUSED) +{ + const char *types[] = {eina_stringshare_add("text/plain")}; + printf("Drag and Drop has droped on the window %p (%d, %d)\n", ee, p.x, p.y); + Eina_Future *f = ecore_evas_selection_get(ee, 0, ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER, EINA_C_ARRAY_ITERATOR_NEW(types)); + eina_future_then(f, _deliverty_cb, .data = ((void*)(intptr_t)ECORE_EVAS_SELECTION_BUFFER_DRAG_AND_DROP_BUFFER)); +} + +static void +_efl_ui_terminated(Ecore_Evas *ee EINA_UNUSED, unsigned int seat EINA_UNUSED, void *data, Eina_Bool accepted EINA_UNUSED) +{ + efl_del(data); +} + +static Eo* +_start_dnd(Ecore_Evas *ee) +{ + Ecore_Evas *ee2; + Eina_Content *content = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL("This is sample content"), "text/plain"); + Efl_Ui_Win *win; + Efl_Ui_Button *btn; + + win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get()); + ee2 = ecore_evas_ecore_evas_get(evas_object_evas_get(win)); + + btn = efl_add(EFL_UI_BUTTON_CLASS, win); + efl_text_set(btn, "Test"); + efl_content_set(win, btn); + + evas_object_geometry_set(win, 0, 0, 100, 100); + + ecore_evas_drag_start(ee, 0, content, ee2, "copy", _efl_ui_terminated, win); + + return win; +} + +static void +_start_op(void *data, const Efl_Event *ev EINA_UNUSED) +{ + _start_dnd(data); +} + +static Eina_Value +_delete_cb(Eo *obj, void *data EINA_UNUSED, const Eina_Value value EINA_UNUSED) +{ + Ecore_Evas *ee ; + ee = ecore_evas_ecore_evas_get(evas_object_evas_get(obj)); + + ecore_evas_drag_cancel(ee, 0); + + return EINA_VALUE_EMPTY; +} + +static void +_start_delayed_del_op(void *data, const Efl_Event *ev EINA_UNUSED) +{ + _start_dnd(data); + efl_future_then(ev->object, efl_loop_timeout(efl_main_loop_get(), 2.0), _delete_cb); +} + +EAPI_MAIN void +efl_main(void *data EINA_UNUSED, const Efl_Event *ev) +{ + Efl_Ui_Textbox *txt, *win, *bx, *btn; + Efl_Loop_Arguments *args = ev->info; + char *goal; + + win = efl_add(EFL_UI_WIN_CLASS, efl_main_loop_get()); + ee = ecore_evas_ecore_evas_get(evas_object_evas_get(win)); + + bx = efl_add(EFL_UI_BOX_CLASS, win); + + txt = efl_add(EFL_UI_TEXTBOX_CLASS, win); + efl_text_set(txt, "Sample for CNP and DND interaction"); + efl_pack_end(bx, txt); + + btn = efl_add(EFL_UI_BUTTON_CLASS, win); + efl_gfx_hint_weight_set(btn, 1.0, 0.0); + efl_event_callback_add(btn, EFL_INPUT_EVENT_PRESSED, _start_op, ee); + efl_text_set(btn, "Start DND op"); + efl_pack_end(bx, btn); + + btn = efl_add(EFL_UI_BUTTON_CLASS, win); + efl_gfx_hint_weight_set(btn, 1.0, 0.0); + efl_event_callback_add(btn, EFL_INPUT_EVENT_PRESSED, _start_delayed_del_op, ee); + efl_text_set(btn, "Start DND op self destroy after 2 sec"); + efl_pack_end(bx, btn); + + efl_content_set(win, bx); + efl_gfx_entity_size_set(win, EINA_SIZE2D(320, 320)); + + goal = eina_array_data_get(args->argv, 1); + + if (eina_streq(goal, "--monitor")) + { + ecore_evas_callback_selection_changed_set(ee, _selection_changed); + ecore_evas_callback_drop_drop_set(ee, _drop_cb); + ecore_evas_callback_drop_motion_set(ee, _motion_cb); + ecore_evas_callback_drop_state_changed_set(ee, _enter_state_change_cb); + } + else if (eina_streq(goal, "--show-selections")) + { + ecore_evas_callback_selection_changed_set(ee, _request_selection); + } + else if (eina_streq(goal, "--set-selection")) + { + if (eina_array_count(args->argv) < 3) + { + printf("Error, --set-selection only requires exactly 1 keyword (The selection to set).\n"); + return; + } + char *selection = eina_array_data_get(args->argv, 2); + Eina_Content *content = eina_content_new((Eina_Slice)EINA_SLICE_STR_FULL(selection), "text/plain"); + ecore_evas_selection_set(ee, 0, ECORE_EVAS_SELECTION_BUFFER_COPY_AND_PASTE_BUFFER, content); + } + else if (eina_streq(goal, "--show-owner")) + { + for (int i = 0; i < ECORE_EVAS_SELECTION_BUFFER_LAST; ++i) + { + printf("Selection buffer %d : %d\n", i, ecore_evas_selection_exists(ee, 0, i)); + } + } + else + { + printf("Error, goal %s not found\n", goal); + } +} +EFL_MAIN() diff --git a/src/tests/elementary/meson.build b/src/tests/elementary/meson.build index 7f5fd03733..1b43da7064 100644 --- a/src/tests/elementary/meson.build +++ b/src/tests/elementary/meson.build @@ -187,6 +187,11 @@ efl_ui_compile_test = executable('efl_ui_compile_test', dependencies: [elementary, eio], ) +executable('efl_ui_window_cnp_dnd_slave', + 'efl_ui_window_cnp_dnd_slave.c', + dependencies: [elementary], +) + test('elementary-suite', elementary_suite, env : test_env )