From f0ba92d39e1f6631974d53100dcbb4cc040a8241 Mon Sep 17 00:00:00 2001 From: MinJeong Kim Date: Wed, 16 Jul 2014 11:48:14 -0400 Subject: [PATCH] Add simple clipboard based on wayland protocol Summary: Whenever any client requests new selection_set, the data is saved to server-side clipboard directly on this revision. So data device is able to keep data which is copied from client app have closed already and is able to provide those data to new focused surface before coming new selection_set request. Reviewers: devilhorns Subscribers: cedric, gwanglim Differential Revision: https://phab.enlightenment.org/D1165 --- src/bin/e_comp_wl.h | 6 + src/bin/e_comp_wl_data.c | 289 +++++++++++++++++++++++++++++++++++++-- src/bin/e_comp_wl_data.h | 22 +++ 3 files changed, 302 insertions(+), 15 deletions(-) diff --git a/src/bin/e_comp_wl.h b/src/bin/e_comp_wl.h index 74c5e3b8b..ce8c219e0 100644 --- a/src/bin/e_comp_wl.h +++ b/src/bin/e_comp_wl.h @@ -120,6 +120,12 @@ struct _E_Comp_Wl_Data struct wl_listener data_source_listener; } selection; + struct + { + void *source; + struct wl_listener listener; + } clipboard; + struct { struct wl_resource *resource; diff --git a/src/bin/e_comp_wl_data.c b/src/bin/e_comp_wl_data.c index 0f931c304..7483a59c5 100644 --- a/src/bin/e_comp_wl_data.c +++ b/src/bin/e_comp_wl_data.c @@ -216,27 +216,25 @@ _e_comp_wl_data_device_data_offer_create(E_Comp_Wl_Data_Source *source, struct w } static void -_e_comp_wl_data_device_cb_drag_start(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource EINA_UNUSED, struct wl_resource *origin_resource EINA_UNUSED, struct wl_resource *icon_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) +_e_comp_wl_data_device_selection_set(E_Comp_Wl_Data *cdata, E_Comp_Wl_Data_Source *source, uint32_t serial) { - -} - -static void -_e_comp_wl_data_device_cb_selection_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) -{ - E_Comp_Wl_Data *cdata; - E_Comp_Wl_Data_Source *source, *sel_source; + E_Comp_Wl_Data_Source *sel_source; struct wl_resource *offer_res, *data_device_res, *focus = NULL; - if (!source_resource) return; - if (!(cdata = wl_resource_get_user_data(resource))) return; - - source = wl_resource_get_user_data(source_resource); sel_source = (E_Comp_Wl_Data_Source*)cdata->selection.data_source; if ((sel_source) && (cdata->selection.serial - serial < UINT32_MAX / 2)) - return; + { + /* TODO: elm_entry is sending too many request on now, + * for those requests, selection.signal is being emitted also a lot. + * when it completes to optimize the entry, it should be checked more. + */ + if (cdata->clipboard.source) + wl_signal_emit(&cdata->selection.signal, cdata); + + return; + } if (sel_source) { @@ -282,7 +280,26 @@ _e_comp_wl_data_device_cb_selection_set(struct wl_client *client EINA_UNUSED, st } } -static const struct wl_data_device_interface _e_data_device_interface = +static void +_e_comp_wl_data_device_cb_drag_start(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource EINA_UNUSED, struct wl_resource *origin_resource EINA_UNUSED, struct wl_resource *icon_resource EINA_UNUSED, uint32_t serial EINA_UNUSED) +{ + +} + +static void +_e_comp_wl_data_device_cb_selection_set(struct wl_client *client EINA_UNUSED, struct wl_resource *resource EINA_UNUSED, struct wl_resource *source_resource, uint32_t serial) +{ + E_Comp_Wl_Data *cdata; + E_Comp_Wl_Data_Source *source; + + if (!source_resource) return; + if (!(cdata = wl_resource_get_user_data(resource))) return; + + source = wl_resource_get_user_data(source_resource); + _e_comp_wl_data_device_selection_set(cdata, source, serial); +} + +static const struct wl_data_device_interface _e_data_device_interface = { _e_comp_wl_data_device_cb_drag_start, _e_comp_wl_data_device_cb_selection_set, @@ -398,6 +415,243 @@ _e_comp_wl_data_cb_bind_manager(struct wl_client *client, void *data, uint32_t v wl_resource_set_implementation(res, &_e_manager_interface, cdata, NULL); } +static void +_e_comp_wl_clipboard_source_unref(E_Comp_Wl_Clipboard_Source *source) +{ + char* t; + + source->ref --; + if (source->ref > 0) return; + + if (source->fd_handler) + { + ecore_main_fd_handler_del(source->fd_handler); + close(source->fd); + } + + EINA_LIST_FREE(source->data_source.mime_types, t) + eina_stringshare_del(t); + + wl_signal_emit(&source->data_source.destroy_signal, &source->data_source); + wl_array_release(&source->contents); + free(source); +} + +static Eina_Bool +_e_comp_wl_clipboard_offer_load(void *data, Ecore_Fd_Handler *handler) +{ + E_Comp_Wl_Clipboard_Offer *offer; + char *p; + size_t size; + int len; + int fd; + + if (!(offer = (E_Comp_Wl_Clipboard_Offer*)data)) + return ECORE_CALLBACK_CANCEL; + + fd = ecore_main_fd_handler_fd_get(handler); + + size = offer->source->contents.size; + p = (char*)offer->source->contents.data; + len = write(fd, p + offer->offset, size - offer->offset); + if (len > 0) offer->offset += len; + + if ((offer->offset == size) || (len <= 0)) + { + close(fd); + ecore_main_fd_handler_del(handler); + _e_comp_wl_clipboard_source_unref(offer->source); + free(offer); + } + + return ECORE_CALLBACK_RENEW; +} + +static void +_e_comp_wl_clipboard_offer_create(E_Comp_Wl_Clipboard_Source* source, int fd) +{ + E_Comp_Wl_Clipboard_Offer *offer; + + offer = E_NEW(E_Comp_Wl_Clipboard_Offer, 1); + + offer->offset = 0; + offer->source = source; + source->ref++; + offer->fd_handler = + ecore_main_fd_handler_add(fd, ECORE_FD_WRITE, + _e_comp_wl_clipboard_offer_load, offer, + NULL, NULL); +} + +static Eina_Bool +_e_comp_wl_clipboard_source_save(void *data, Ecore_Fd_Handler *handler) +{ + E_Comp_Wl_Data *cdata; + E_Comp_Wl_Clipboard_Source *source; + char *p; + int len, size; + + if (!(cdata = (E_Comp_Wl_Data*)data)) + return ECORE_CALLBACK_CANCEL; + if (!(source = (E_Comp_Wl_Clipboard_Source*)cdata->clipboard.source)) + return ECORE_CALLBACK_CANCEL; + + /* extend contents buffer */ + if ((source->contents.alloc - source->contents.size) < CLIPBOARD_CHUNK) + { + wl_array_add(&source->contents, CLIPBOARD_CHUNK); + source->contents.size -= CLIPBOARD_CHUNK; + } + + p = (char*)source->contents.data + source->contents.size; + size = source->contents.alloc - source->contents.size; + len = read(source->fd, p, size); + + if (len == 0) + { + ecore_main_fd_handler_del(handler); + close(source->fd); + source->fd_handler = NULL; + } + else if (len < 0) + { + _e_comp_wl_clipboard_source_unref(source); + cdata->clipboard.source = NULL; + } + else + { + source->contents.size += len; + } + + return ECORE_CALLBACK_RENEW; +} + +static void +_e_comp_wl_clipboard_source_target_send(E_Comp_Wl_Data_Source *source EINA_UNUSED, uint32_t serial EINA_UNUSED, const char *mime_type EINA_UNUSED) +{ +} + +static void +_e_comp_wl_clipboard_source_send_send(E_Comp_Wl_Data_Source *source, const char *mime_type, int fd) +{ + E_Comp_Wl_Clipboard_Source* clip_source; + char *t; + + if (!source) return; + + clip_source = container_of(source, E_Comp_Wl_Clipboard_Source, data_source); + if (!clip_source) return; + + t = eina_list_nth(source->mime_types, 0); + if (!strcmp(mime_type, t)) + _e_comp_wl_clipboard_offer_create(clip_source, fd); + else + close(fd); +} + +static void +_e_comp_wl_clipboard_source_cancelled_send(E_Comp_Wl_Data_Source *source EINA_UNUSED) +{ +} + +static E_Comp_Wl_Clipboard_Source* +_e_comp_wl_clipboard_source_create(E_Comp_Wl_Data *cdata, const char *mime_type, uint32_t serial, int fd) +{ + E_Comp_Wl_Clipboard_Source *source; + + source = E_NEW(E_Comp_Wl_Clipboard_Source, 1); + if (!source) return NULL; + + source->data_source.resource = NULL; + source->data_source.target = _e_comp_wl_clipboard_source_target_send; + source->data_source.send = _e_comp_wl_clipboard_source_send_send; + source->data_source.cancelled = _e_comp_wl_clipboard_source_cancelled_send; + + wl_array_init(&source->contents); + wl_signal_init(&source->data_source.destroy_signal); + + source->ref = 1; + source->serial = serial; + + source->data_source.mime_types = + eina_list_append(source->data_source.mime_types, + eina_stringshare_add(mime_type)); + + source->fd_handler = + ecore_main_fd_handler_add(fd, ECORE_FD_READ, + _e_comp_wl_clipboard_source_save, + cdata, NULL, NULL); + if (!source->fd_handler) return NULL; + + source->fd = fd; + + return source; +} + +static void +_e_comp_wl_clipboard_selection_set(struct wl_listener *listener EINA_UNUSED, void *data) +{ + E_Comp_Wl_Data *cdata; + E_Comp_Wl_Data_Source *sel_source; + E_Comp_Wl_Clipboard_Source *clip_source; + int p[2]; + char *mime_type; + + if (!(cdata = (E_Comp_Wl_Data*)data)) return; + + sel_source = (E_Comp_Wl_Data_Source*) cdata->selection.data_source; + clip_source = (E_Comp_Wl_Clipboard_Source*) cdata->clipboard.source; + + if (!sel_source) + { + if (clip_source) + _e_comp_wl_data_device_selection_set(cdata, + &clip_source->data_source, + clip_source->serial); + return; + } + else if (sel_source->target == _e_comp_wl_clipboard_source_target_send) + return; + + if (clip_source) + _e_comp_wl_clipboard_source_unref(clip_source); + + cdata->clipboard.source = NULL; + mime_type = eina_list_nth(sel_source->mime_types, 0); + + if (pipe2(p, O_CLOEXEC) == -1) + return; + + sel_source->send(sel_source, mime_type, p[1]); + + cdata->clipboard.source = + _e_comp_wl_clipboard_source_create(cdata, mime_type, + cdata->selection.serial, p[0]); + + if (!cdata->clipboard.source) + { + close(p[0]); + return; + } +} + +static void +_e_comp_wl_clipboard_destroy(E_Comp_Wl_Data *cdata) +{ + if (!cdata) return; + + wl_list_remove(&cdata->clipboard.listener.link); +} + +static void +_e_comp_wl_clipboard_create(E_Comp_Wl_Data *cdata) +{ + if (!cdata) return; + + cdata->clipboard.listener.notify = _e_comp_wl_clipboard_selection_set; + wl_signal_add(&cdata->selection.signal, &cdata->clipboard.listener); +} + EINTERN void e_comp_wl_data_device_keyboard_focus_set(E_Comp_Wl_Data *cdata) { @@ -441,6 +695,9 @@ e_comp_wl_data_manager_init(E_Comp_Wl_Data *cdata) wl_signal_init(&cdata->selection.signal); + /* create clipboard */ + _e_comp_wl_clipboard_create(cdata); + return EINA_TRUE; } @@ -449,4 +706,6 @@ e_comp_wl_data_manager_shutdown(E_Comp_Wl_Data *cdata EINA_UNUSED) { /* destroy the global manager resource */ /* if (cdata->mgr.global) wl_global_destroy(cdata->mgr.global); */ + + _e_comp_wl_clipboard_destroy(cdata); } diff --git a/src/bin/e_comp_wl_data.h b/src/bin/e_comp_wl_data.h index e2ae0a7f3..3a19d44db 100644 --- a/src/bin/e_comp_wl_data.h +++ b/src/bin/e_comp_wl_data.h @@ -5,6 +5,10 @@ typedef struct _E_Comp_Wl_Data_Source E_Comp_Wl_Data_Source; typedef struct _E_Comp_Wl_Data_Offer E_Comp_Wl_Data_Offer; +typedef struct _E_Comp_Wl_Clipboard_Source E_Comp_Wl_Clipboard_Source; +typedef struct _E_Comp_Wl_Clipboard_Offer E_Comp_Wl_Clipboard_Offer; + +#define CLIPBOARD_CHUNK 1024 struct _E_Comp_Wl_Data_Source { @@ -26,6 +30,24 @@ struct _E_Comp_Wl_Data_Offer struct wl_listener source_destroy_listener; //listener for destroy of source }; +struct _E_Comp_Wl_Clipboard_Source +{ + E_Comp_Wl_Data_Source data_source; + Ecore_Fd_Handler *fd_handler; + uint32_t serial; + + struct wl_array contents; //for extendable buffer + int ref; + int fd; +}; + +struct _E_Comp_Wl_Clipboard_Offer +{ + E_Comp_Wl_Clipboard_Source *source; + Ecore_Fd_Handler *fd_handler; + size_t offset; +}; + EINTERN void e_comp_wl_data_device_keyboard_focus_set(E_Comp_Wl_Data *cdata); EINTERN Eina_Bool e_comp_wl_data_manager_init(E_Comp_Wl_Data *cdata); EINTERN void e_comp_wl_data_manager_shutdown(E_Comp_Wl_Data *cdata);