From e7d8ab323cd886ca75314748c52e597cf220176f Mon Sep 17 00:00:00 2001 From: Christopher Michael Date: Wed, 29 Aug 2012 06:35:02 +0000 Subject: [PATCH] Ecore_Wayland: Add Copy-n-Paste support for Ecore Wayland. NB: Based on initial work from Alex (thanks for the patch), but modified to clean up compiler warnings, fixed function declarations, fixed function namespacing. SVN revision: 75809 --- .../src/lib/ecore_wayland/Ecore_Wayland.h | 30 ++ legacy/ecore/src/lib/ecore_wayland/ecore_wl.c | 14 + .../src/lib/ecore_wayland/ecore_wl_dnd.c | 276 ++++++++++++++++++ .../src/lib/ecore_wayland/ecore_wl_input.c | 17 +- .../src/lib/ecore_wayland/ecore_wl_private.h | 14 + 5 files changed, 350 insertions(+), 1 deletion(-) diff --git a/legacy/ecore/src/lib/ecore_wayland/Ecore_Wayland.h b/legacy/ecore/src/lib/ecore_wayland/Ecore_Wayland.h index 025357cfea..02f3c1a6ce 100644 --- a/legacy/ecore/src/lib/ecore_wayland/Ecore_Wayland.h +++ b/legacy/ecore/src/lib/ecore_wayland/Ecore_Wayland.h @@ -35,6 +35,7 @@ typedef struct _Ecore_Wl_Input Ecore_Wl_Input; # ifndef _ECORE_WAYLAND_WINDOW_PREDEF typedef struct _Ecore_Wl_Window Ecore_Wl_Window; # endif +typedef struct _Ecore_Wl_Dnd Ecore_Wl_Dnd; /** @since 1.7 */ typedef struct _Ecore_Wl_Dnd_Source Ecore_Wl_Dnd_Source; typedef struct _Ecore_Wl_Dnd_Target Ecore_Wl_Dnd_Target; @@ -47,6 +48,8 @@ typedef struct _Ecore_Wl_Event_Dnd_Enter Ecore_Wl_Event_Dnd_Enter; typedef struct _Ecore_Wl_Event_Dnd_Position Ecore_Wl_Event_Dnd_Position; typedef struct _Ecore_Wl_Event_Dnd_Leave Ecore_Wl_Event_Dnd_Leave; typedef struct _Ecore_Wl_Event_Dnd_Drop Ecore_Wl_Event_Dnd_Drop; +typedef struct _Ecore_Wl_Event_Data_Source_Send Ecore_Wl_Event_Data_Source_Send; /** @since 1.7 */ +typedef struct _Ecore_Wl_Event_Selection_Data_Ready Ecore_Wl_Event_Selection_Data_Ready; /** @since 1.7 */ typedef struct _Ecore_Wl_Event_Interfaces_Bound Ecore_Wl_Event_Interfaces_Bound; enum _Ecore_Wl_Window_Type @@ -143,6 +146,7 @@ struct _Ecore_Wl_Input Ecore_Wl_Dnd_Source *drag_source; Ecore_Wl_Dnd_Source *selection_source; + Ecore_Wl_Dnd *dnd; /** @since 1.7 */ struct { @@ -281,6 +285,21 @@ struct _Ecore_Wl_Event_Dnd_Drop } position; }; +/** @since 1.7 */ +struct _Ecore_Wl_Event_Data_Source_Send +{ + char *type; + int fd; +}; + +/** @since 1.7 */ +struct _Ecore_Wl_Event_Selection_Data_Ready +{ + char *data; + int len; + Eina_Bool done; +}; + struct _Ecore_Wl_Event_Interfaces_Bound { Eina_Bool compositor : 1; @@ -311,6 +330,10 @@ EAPI extern int ECORE_WL_EVENT_DND_ENTER; EAPI extern int ECORE_WL_EVENT_DND_POSITION; EAPI extern int ECORE_WL_EVENT_DND_LEAVE; EAPI extern int ECORE_WL_EVENT_DND_DROP; +EAPI extern int ECORE_WL_EVENT_DATA_SOURCE_TARGET; /** @since 1.7 */ +EAPI extern int ECORE_WL_EVENT_DATA_SOURCE_SEND; /** @since 1.7 */ +EAPI extern int ECORE_WL_EVENT_DATA_SOURCE_CANCELLED; /** @since 1.7 */ +EAPI extern int ECORE_WL_EVENT_SELECTION_DATA_READY; /** @since 1.7 */ EAPI extern int ECORE_WL_EVENT_INTERFACES_BOUND; EAPI int ecore_wl_init(const char *name); @@ -356,4 +379,11 @@ EAPI void ecore_wl_window_cursor_from_name_set(Ecore_Wl_Window *win, const char EAPI void ecore_wl_window_cursor_default_restore(Ecore_Wl_Window *win); EAPI void ecore_wl_window_parent_set(Ecore_Wl_Window *win, Ecore_Wl_Window *parent); +/** @since 1.7 */ +EAPI Eina_Bool ecore_wl_dnd_set_selection(Ecore_Wl_Dnd *dnd, const char **types_offered); +EAPI Eina_Bool ecore_wl_dnd_get_selection(Ecore_Wl_Dnd *dnd, const char *type); +EAPI Ecore_Wl_Dnd *ecore_wl_dnd_get(); +EAPI Eina_Bool ecore_wl_dnd_start_drag(); +EAPI Eina_Bool ecore_wl_dnd_selection_has_owner(Ecore_Wl_Dnd *dnd); + #endif diff --git a/legacy/ecore/src/lib/ecore_wayland/ecore_wl.c b/legacy/ecore/src/lib/ecore_wayland/ecore_wl.c index 0a68777b99..3bb05e76dd 100644 --- a/legacy/ecore/src/lib/ecore_wayland/ecore_wl.c +++ b/legacy/ecore/src/lib/ecore_wayland/ecore_wl.c @@ -29,6 +29,10 @@ EAPI int ECORE_WL_EVENT_DND_ENTER = 0; EAPI int ECORE_WL_EVENT_DND_POSITION = 0; EAPI int ECORE_WL_EVENT_DND_LEAVE = 0; EAPI int ECORE_WL_EVENT_DND_DROP = 0; +EAPI int ECORE_WL_EVENT_DATA_SOURCE_TARGET = 0; +EAPI int ECORE_WL_EVENT_DATA_SOURCE_SEND = 0; +EAPI int ECORE_WL_EVENT_SELECTION_DATA_READY = 0; +EAPI int ECORE_WL_EVENT_DATA_SOURCE_CANCELLED = 0; EAPI int ECORE_WL_EVENT_INTERFACES_BOUND = 0; /** @@ -95,6 +99,10 @@ ecore_wl_init(const char *name) ECORE_WL_EVENT_DND_POSITION = ecore_event_type_new(); ECORE_WL_EVENT_DND_LEAVE = ecore_event_type_new(); ECORE_WL_EVENT_DND_DROP = ecore_event_type_new(); + ECORE_WL_EVENT_DATA_SOURCE_TARGET = ecore_event_type_new(); + ECORE_WL_EVENT_DATA_SOURCE_SEND = ecore_event_type_new(); + ECORE_WL_EVENT_SELECTION_DATA_READY = ecore_event_type_new(); + ECORE_WL_EVENT_DATA_SOURCE_CANCELLED = ecore_event_type_new(); ECORE_WL_EVENT_INTERFACES_BOUND = ecore_event_type_new(); } @@ -482,3 +490,9 @@ _ecore_wl_xkb_shutdown(Ecore_Wl_Display *ewd) return EINA_TRUE; } + +struct wl_data_source * +_ecore_wl_create_data_source(Ecore_Wl_Display *ewd) +{ + return wl_data_device_manager_create_data_source(ewd->wl.data_device_manager); +} diff --git a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_dnd.c b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_dnd.c index fbf807517b..e7c8f86969 100644 --- a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_dnd.c +++ b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_dnd.c @@ -2,18 +2,162 @@ # include #endif +#include +#include #include "ecore_wl_private.h" +struct _dnd_task +{ + void *data; + Ecore_Fd_Cb cb; +}; + +struct _dnd_read_ctx +{ + int epoll_fd; + struct epoll_event *ep; +}; + /* local function prototypes */ static void _ecore_wl_dnd_offer(void *data, struct wl_data_offer *wl_data_offer __UNUSED__, const char *type); static void _ecore_wl_dnd_cb_enter_free(void *data __UNUSED__, void *event); +static void _ecore_wl_dnd_data_source_target(void *data, struct wl_data_source *source, const char *mime_type); +static void _ecore_wl_dnd_data_source_send(void *data, struct wl_data_source *source, const char *mime_type, int32_t fd); +static void _ecore_wl_dnd_data_source_cancelled(void *data, struct wl_data_source *source); +static void _ecore_wl_dnd_source_receive_data(Ecore_Wl_Dnd_Source *source, const char *type); + /* wayland listeners */ static const struct wl_data_offer_listener _ecore_wl_data_offer_listener = { _ecore_wl_dnd_offer, }; +static const struct wl_data_source_listener _ecore_wl_data_source_listener = +{ + _ecore_wl_dnd_data_source_target, + _ecore_wl_dnd_data_source_send, + _ecore_wl_dnd_data_source_cancelled +}; + +extern Ecore_Wl_Dnd *glb_dnd; + +EAPI Ecore_Wl_Dnd * +ecore_wl_dnd_get() +{ + return glb_dnd; +} + +EAPI Eina_Bool +ecore_wl_dnd_start_drag(Ecore_Wl_Dnd *dnd __UNUSED__) +{ + //TODO: + return EINA_TRUE; +} + +EAPI Eina_Bool +ecore_wl_dnd_set_selection(Ecore_Wl_Dnd *dnd, const char **types_offered) +{ + char **p; + const char **type; + + dnd->data_source = _ecore_wl_create_data_source(dnd->ewd); + + /* free old types */ + if (dnd->types_offered.data) + { + wl_array_for_each(p, &dnd->types_offered) + free(*p); + wl_array_release(&dnd->types_offered); + wl_array_init(&dnd->types_offered); + } + + for (type = types_offered; *type; type++) + { + p = wl_array_add(&dnd->types_offered, sizeof(*p)); + *p = strdup(*type); + wl_data_source_offer(dnd->data_source, *p); + } + + wl_data_source_add_listener(dnd->data_source, &_ecore_wl_data_source_listener, dnd); + + _ecore_wl_input_set_selection(dnd->input, dnd->data_source); + + return EINA_TRUE; +} + +EAPI Eina_Bool +ecore_wl_dnd_get_selection(Ecore_Wl_Dnd *dnd, const char *type) +{ + char **p; + Ecore_Wl_Input *input; + + input = dnd->input; + + if (!input->selection_source) return EINA_FALSE; + + wl_array_for_each(p, &input->selection_source->types) + if (strcmp(type, *p) == 0) break; + + if (!*p) return EINA_FALSE; + + _ecore_wl_dnd_source_receive_data(input->selection_source, type); + + return EINA_TRUE; +} + +EAPI Eina_Bool +ecore_wl_dnd_selection_has_owner(Ecore_Wl_Dnd *dnd) +{ + Ecore_Wl_Input *input; + + input = dnd->input; + return (input->selection_source != NULL); +} + +/* local functions */ +static void +_ecore_wl_dnd_data_source_target(void *data __UNUSED__, struct wl_data_source *source __UNUSED__, const char *mime_type __UNUSED__) +{ + //TODO: +} + +static void +_ecore_wl_dnd_cb_data_source_send_free(void *data __UNUSED__, void *event) +{ + Ecore_Wl_Event_Data_Source_Send *ev; + + LOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(ev = event)) return; + + free(ev->type); + free(ev); +} + +static void +_ecore_wl_dnd_data_source_send(void *data, struct wl_data_source *source __UNUSED__, const char *mime_type, int32_t fd) +{ + Ecore_Wl_Event_Data_Source_Send *event; + + LOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!data) return; + + if (!(event = calloc(1, sizeof(Ecore_Wl_Event_Data_Source_Send)))) return; + + event->type = strdup(mime_type); + event->fd = fd; + + ecore_event_add(ECORE_WL_EVENT_DATA_SOURCE_SEND, event, _ecore_wl_dnd_cb_data_source_send_free, NULL); +} + +static void +_ecore_wl_dnd_data_source_cancelled(void *data __UNUSED__, struct wl_data_source *source) +{ + wl_data_source_destroy(source); +} + void _ecore_wl_dnd_add(Ecore_Wl_Input *input, struct wl_data_device *data_device __UNUSED__, struct wl_data_offer *offer) { @@ -207,3 +351,135 @@ _ecore_wl_dnd_cb_enter_free(void *data __UNUSED__, void *event) if (!(ev = event)) return; free(ev); } + +static void +_ecore_wl_dnd_cb_selection_data_ready_free(void *data __UNUSED__, void *event) +{ + Ecore_Wl_Event_Selection_Data_Ready *ev; + + LOGFN(__FILE__, __LINE__, __FUNCTION__); + + if (!(ev = event)) return; + + free(ev->data); + free(ev); +} + +static Eina_Bool +_ecore_wl_dnd_read_data(void *data, Ecore_Fd_Handler *fd_handler __UNUSED__) +{ + int len; + char buffer[4096]; + Ecore_Wl_Dnd_Source *source; + Ecore_Wl_Event_Selection_Data_Ready *event; + Eina_Bool ret; + + source = data; + + len = read(source->fd, buffer, sizeof buffer); + + if (!(event = calloc(1, sizeof(Ecore_Wl_Event_Selection_Data_Ready)))) + return ECORE_CALLBACK_CANCEL; + + if (len <= 0) + { + close(source->fd); + _ecore_wl_dnd_del(source); + event->done = EINA_TRUE; + event->data = NULL; + event->len = 0; + ret = ECORE_CALLBACK_CANCEL; + } + else + { + event->data = malloc(len + 1); + if (!event->data) return ECORE_CALLBACK_CANCEL; + strncpy(event->data, buffer, len); + event->data[len] = '\0'; + event->len = len; + event->done = EINA_FALSE; + ret = ECORE_CALLBACK_RENEW; + } + + ecore_event_add(ECORE_WL_EVENT_SELECTION_DATA_READY, event, + _ecore_wl_dnd_cb_selection_data_ready_free, NULL); + return ret; +} + + +static Eina_Bool +_ecore_wl_dnd_idler_cb(void *data) +{ + struct _dnd_read_ctx *ctx; + struct _dnd_task *task; + int count, i; + + ctx = data; + count = epoll_wait(ctx->epoll_fd, ctx->ep, 1, 0); + for (i = 0; i < count; i++) + { + task = ctx->ep->data.ptr; + if (task->cb(task->data, NULL) == ECORE_CALLBACK_CANCEL) + { + free(ctx->ep); + free(task); + free(ctx); + return ECORE_CALLBACK_CANCEL; + } + } + return ECORE_CALLBACK_RENEW; +} + +static void +_ecore_wl_dnd_source_receive_data(Ecore_Wl_Dnd_Source *source, const char *type) +{ + int epoll_fd; + struct epoll_event *ep = NULL; + struct _dnd_task *task = NULL; + struct _dnd_read_ctx *read_ctx = NULL; + int p[2]; + + if (pipe2(p, O_CLOEXEC) == -1) + return; + + wl_data_offer_receive(source->offer, type, p[1]); + close(p[1]); + + /* Due to http://trac.enlightenment.org/e/ticket/1208, + * use epoll and idle handler instead of ecore_main_fd_handler_add() */ + + ep = calloc(1, sizeof(struct epoll_event)); + if (!ep) goto err; + + task = calloc(1, sizeof(struct _dnd_task)); + if (!task) goto err; + + read_ctx = calloc(1, sizeof(struct _dnd_read_ctx)); + if (!read_ctx) goto err; + + epoll_fd = epoll_create1(0); + if (epoll_fd < 0) goto err; + + task->data = source; + task->cb = _ecore_wl_dnd_read_data; + ep->events = EPOLLIN; + ep->data.ptr = task; + + if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, p[0], ep) < 0) goto err; + + read_ctx->epoll_fd = epoll_fd; + read_ctx->ep = ep; + + if (!ecore_idler_add(_ecore_wl_dnd_idler_cb, read_ctx)) goto err; + + source->refcount++; + source->fd = p[0]; + return; + +err: + if (ep) free(ep); + if (task) free(task); + if (read_ctx) free(read_ctx); + close(p[0]); + return; +} diff --git a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_input.c b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_input.c index 176c173518..ccb4e5b002 100644 --- a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_input.c +++ b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_input.c @@ -39,6 +39,8 @@ #define MOD_ALT_MASK 0x02 #define MOD_CONTROL_MASK 0x04 +Ecore_Wl_Dnd *glb_dnd = NULL; + /* local function prototypes */ static void _ecore_wl_input_seat_handle_capabilities(void *data, struct wl_seat *seat, enum wl_seat_capability caps); @@ -110,7 +112,6 @@ static const struct wl_seat_listener _ecore_wl_seat_listener = _ecore_wl_input_seat_handle_capabilities, }; - static const struct wl_data_device_listener _ecore_wl_data_listener = { _ecore_wl_input_cb_data_offer, @@ -256,6 +257,14 @@ _ecore_wl_input_add(Ecore_Wl_Display *ewd, unsigned int id) NULL, NULL); ewd->input = input; + + /* create Ecore_Wl_Dnd */ + if (!glb_dnd) + if (!(glb_dnd = calloc(1, sizeof(Ecore_Wl_Dnd)))) return; + glb_dnd->ewd = ewd; + glb_dnd->input = input; + input->dnd = glb_dnd; + wl_array_init(&glb_dnd->types_offered); } void @@ -1193,3 +1202,9 @@ _ecore_wl_input_mouse_wheel_send(Ecore_Wl_Input *input, unsigned int axis, int v ecore_event_add(ECORE_EVENT_MOUSE_WHEEL, ev, NULL, NULL); } + +void +_ecore_wl_input_set_selection(Ecore_Wl_Input *input, struct wl_data_source *source) +{ + wl_data_device_set_selection(input->data_device, source, input->display->serial); +} diff --git a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_private.h b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_private.h index 63a5f12e30..7f59ae9bcd 100644 --- a/legacy/ecore/src/lib/ecore_wayland/ecore_wl_private.h +++ b/legacy/ecore/src/lib/ecore_wayland/ecore_wl_private.h @@ -50,6 +50,18 @@ extern Ecore_Wl_Display *_ecore_wl_disp; # endif # define CRIT(...) EINA_LOG_DOM_CRIT(_ecore_wl_log_dom, __VA_ARGS__) +struct _Ecore_Wl_Dnd +{ + Ecore_Wl_Display *ewd; + Ecore_Wl_Input *input; + + /* As provider */ + struct wl_data_source *data_source; + struct wl_array types_offered; + + /* TODO: dnd specific fields */ +}; + struct _Ecore_Wl_Dnd_Source { struct wl_data_offer *offer; @@ -77,6 +89,7 @@ void _ecore_wl_output_del(Ecore_Wl_Output *output); void _ecore_wl_input_add(Ecore_Wl_Display *ewd, unsigned int id); void _ecore_wl_input_del(Ecore_Wl_Input *input); void _ecore_wl_input_pointer_xy_get(int *x, int *y); +void _ecore_wl_input_set_selection(Ecore_Wl_Input *input, struct wl_data_source *source); void _ecore_wl_dnd_add(Ecore_Wl_Input *input, struct wl_data_device *data_device, struct wl_data_offer *offer); void _ecore_wl_dnd_enter(void *data, struct wl_data_device *data_device __UNUSED__, unsigned int timestamp __UNUSED__, struct wl_surface *surface, int x, int y, struct wl_data_offer *offer); @@ -86,4 +99,5 @@ void _ecore_wl_dnd_drop(void *data, struct wl_data_device *data_device __UNUSED_ void _ecore_wl_dnd_selection(void *data, struct wl_data_device *data_device __UNUSED__, struct wl_data_offer *offer); void _ecore_wl_dnd_del(Ecore_Wl_Dnd_Source *source); +struct wl_data_source *_ecore_wl_create_data_source(Ecore_Wl_Display *ewd); #endif